Skip to content

Commit 325aaeb

Browse files
author
hardcoded
committed
PythonException now has a meaningful message (The message of the actual python exception that was raised).
1 parent 2197e7a commit 325aaeb

5 files changed

Lines changed: 178 additions & 108 deletions

File tree

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
<NUnitProject>
2-
<Settings activeconfig="Debug" />
3-
<Config name="Debug" binpathtype="Auto">
4-
<assembly path="bin\Release\EmbeddingTest.dll" />
5-
</Config>
6-
<Config name="Release" binpathtype="Auto" />
1+
<NUnitProject>
2+
<Settings activeconfig="Debug" />
3+
<Config name="Debug" binpathtype="Auto">
4+
<assembly path="bin\Release\Python.EmbeddingTest.dll" />
5+
</Config>
6+
<Config name="Release" binpathtype="Auto" />
77
</NUnitProject>

pythonnet/src/embed_tests/pyobject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using NUnit.Framework;
33
using Python.Runtime;
44

5-
namespace EmbeddingTest
5+
namespace Python.EmbeddingTest
66
{
77
[TestFixture]
88
public class PyObjectTest
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using NUnit.Framework;
3+
using Python.Runtime;
4+
5+
namespace Python.EmbeddingTest
6+
{
7+
[TestFixture]
8+
public class PythonExceptionTest
9+
{
10+
private IntPtr gs;
11+
12+
[SetUp]
13+
public void SetUp()
14+
{
15+
PythonEngine.Initialize();
16+
gs = PythonEngine.AcquireLock();
17+
}
18+
19+
[TearDown]
20+
public void TearDown()
21+
{
22+
PythonEngine.ReleaseLock(gs);
23+
PythonEngine.Shutdown();
24+
}
25+
26+
[Test]
27+
public void TestMessage()
28+
{
29+
PyList list = new PyList();
30+
try
31+
{
32+
PyObject junk = list[0];
33+
}
34+
catch (PythonException e)
35+
{
36+
Assert.AreEqual("exceptions.IndexError : list index out of range", e.Message);
37+
}
38+
}
39+
40+
[Test]
41+
public void TestNoError()
42+
{
43+
PythonException e = new PythonException(); //There is no PyErr to fetch
44+
Assert.AreEqual("", e.Message);
45+
}
46+
}
47+
}

pythonnet/src/runtime/exceptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public static void SetError(Exception e) {
298298

299299
PythonException pe = e as PythonException;
300300
if (pe != null) {
301-
Runtime.PyErr_SetObject(pe.Type, pe.Value);
301+
Runtime.PyErr_SetObject(pe.PyType, pe.PyValue);
302302
return;
303303
}
304304

pythonnet/src/runtime/pythonexception.cs

Lines changed: 123 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -18,107 +18,130 @@ namespace Python.Runtime {
1818

1919
public class PythonException : System.Exception {
2020

21-
private IntPtr excType = IntPtr.Zero;
22-
private IntPtr excValue = IntPtr.Zero;
23-
private IntPtr excTb = IntPtr.Zero;
24-
private bool disposed = false;
25-
26-
public PythonException() : base() {
27-
Runtime.PyErr_Fetch(ref excType, ref excValue, ref excTb);
28-
Runtime.Incref(excType);
29-
Runtime.Incref(excValue);
30-
Runtime.Incref(excTb);
31-
32-
}
33-
34-
// Ensure that encapsulated Python objects are decref'ed appropriately
35-
// when the managed exception wrapper is garbage-collected.
36-
37-
~PythonException() {
38-
Dispose();
39-
}
40-
41-
42-
/// <summary>
43-
/// Type Property
44-
/// </summary>
45-
///
46-
/// <remarks>
47-
/// Returns the exception type as a Python object.
48-
/// </remarks>
49-
50-
public IntPtr Type {
51-
get {
52-
return excType;
53-
}
54-
}
55-
56-
/// <summary>
57-
/// Value Property
58-
/// </summary>
59-
///
60-
/// <remarks>
61-
/// Returns the exception value as a Python object.
62-
/// </remarks>
63-
64-
public IntPtr Value {
65-
get {
66-
return excValue;
67-
}
68-
}
69-
70-
/// <summary>
71-
/// Traceback Property
72-
/// </summary>
73-
///
74-
/// <remarks>
75-
/// Returns the exception traceback as a Python object.
76-
/// </remarks>
77-
78-
public IntPtr Traceback {
79-
get {
80-
return excTb;
81-
}
82-
}
83-
84-
85-
/// <summary>
86-
/// Dispose Method
87-
/// </summary>
88-
///
89-
/// <remarks>
90-
/// The Dispose method provides a way to explicitly release the
91-
/// Python objects represented by a PythonException.
92-
/// </remarks>
93-
94-
public void Dispose() {
95-
if (!disposed) {
96-
if (Runtime.Py_IsInitialized() > 0) {
97-
IntPtr gs = PythonEngine.AcquireLock();
98-
Runtime.Decref(excType);
99-
Runtime.Decref(excValue);
100-
Runtime.Decref(excTb);
101-
PythonEngine.ReleaseLock(gs);
102-
}
103-
GC.SuppressFinalize(this);
104-
disposed = true;
105-
}
106-
}
107-
108-
/// <summary>
109-
/// Matches Method
110-
/// </summary>
111-
///
112-
/// <remarks>
113-
/// Returns true if the Python exception type represented by the
114-
/// PythonException instance matches the given exception type.
115-
/// </remarks>
116-
117-
public static bool Matches(IntPtr ob) {
118-
return Runtime.PyErr_ExceptionMatches(ob) != 0;
119-
}
21+
private IntPtr _pyType = IntPtr.Zero;
22+
private IntPtr _pyValue = IntPtr.Zero;
23+
private IntPtr _pyTB = IntPtr.Zero;
24+
private string _tb = "";
25+
private string _message = "";
26+
private bool disposed = false;
27+
28+
public PythonException() : base()
29+
{
30+
Runtime.PyErr_Fetch(ref _pyType, ref _pyValue, ref _pyTB);
31+
Runtime.Incref(_pyType);
32+
Runtime.Incref(_pyValue);
33+
Runtime.Incref(_pyTB);
34+
IntPtr gs = PythonEngine.AcquireLock();
35+
if ((_pyType != IntPtr.Zero) && (_pyValue != IntPtr.Zero))
36+
{
37+
string type = new PyObject(_pyType).ToString();
38+
string message = Runtime.GetManagedString(_pyValue);
39+
_message = type + " : " + message;
40+
}
41+
if (_pyTB != IntPtr.Zero)
42+
{
43+
PyObject tb_module = PythonEngine.ImportModule("traceback");
44+
_tb = tb_module.InvokeMethod("format_tb", new PyObject(_pyTB)).ToString();
45+
}
46+
PythonEngine.ReleaseLock(gs);
47+
}
48+
49+
// Ensure that encapsulated Python objects are decref'ed appropriately
50+
// when the managed exception wrapper is garbage-collected.
51+
52+
~PythonException() {
53+
Dispose();
54+
}
12055

121-
}
12256

57+
/// <summary>
58+
/// PyType Property
59+
/// </summary>
60+
///
61+
/// <remarks>
62+
/// Returns the exception type as a Python object.
63+
/// </remarks>
64+
65+
public IntPtr PyType
66+
{
67+
get { return _pyType; }
68+
}
69+
70+
/// <summary>
71+
/// PyValue Property
72+
/// </summary>
73+
///
74+
/// <remarks>
75+
/// Returns the exception value as a Python object.
76+
/// </remarks>
12377

78+
public IntPtr PyValue
79+
{
80+
get { return _pyValue; }
81+
}
82+
83+
/// <summary>
84+
/// Message Property
85+
/// </summary>
86+
///
87+
/// <remarks>
88+
/// A string representing the python exception message.
89+
/// </remarks>
90+
91+
public override string Message
92+
{
93+
get { return _message; }
94+
}
95+
96+
/// <summary>
97+
/// StackTrace Property
98+
/// </summary>
99+
///
100+
/// <remarks>
101+
/// A string representing the python exception stack trace.
102+
/// </remarks>
103+
104+
public override string StackTrace
105+
{
106+
get { return _tb; }
107+
}
108+
109+
110+
/// <summary>
111+
/// Dispose Method
112+
/// </summary>
113+
///
114+
/// <remarks>
115+
/// The Dispose method provides a way to explicitly release the
116+
/// Python objects represented by a PythonException.
117+
/// </remarks>
118+
119+
public void Dispose() {
120+
if (!disposed) {
121+
if (Runtime.Py_IsInitialized() > 0) {
122+
IntPtr gs = PythonEngine.AcquireLock();
123+
Runtime.Decref(_pyType);
124+
Runtime.Decref(_pyValue);
125+
Runtime.Decref(_pyTB);
126+
PythonEngine.ReleaseLock(gs);
127+
}
128+
GC.SuppressFinalize(this);
129+
disposed = true;
130+
}
131+
}
132+
133+
/// <summary>
134+
/// Matches Method
135+
/// </summary>
136+
///
137+
/// <remarks>
138+
/// Returns true if the Python exception type represented by the
139+
/// PythonException instance matches the given exception type.
140+
/// </remarks>
141+
142+
public static bool Matches(IntPtr ob) {
143+
return Runtime.PyErr_ExceptionMatches(ob) != 0;
144+
}
145+
146+
}
124147
}

0 commit comments

Comments
 (0)