Skip to content

Commit c0fe430

Browse files
committed
reworked PythonException:
Removed private fields, apart from ones returned by `PyErr_Fetch`. Corresponding property values are now generated on demand. Added FetchCurrent*Raw for internal consumption. `PythonException.Type` is now of type `PyType`. Use C API functions `PyException_GetCause` and `PyException_GetTraceback` instead of trying to read via attributes by name. `PythonException` instances are no longer disposable. You can still dispose `.Type`, `.Value` and `.Traceback`, but it is not recommended, as they may be shared with other instances.
1 parent 257a765 commit c0fe430

31 files changed

+415
-334
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ One must now either use enum members (e.g. `MyEnum.Option`), or use enum constru
4949
support for .NET Core
5050
- .NET and Python exceptions are preserved when crossing Python/.NET boundary
5151
- BREAKING: custom encoders are no longer called for instances of `System.Type`
52+
- `PythonException.Restore` no longer clears `PythonException` instance.
5253

5354
### Fixed
5455

@@ -74,6 +75,7 @@ One must now either use enum members (e.g. `MyEnum.Option`), or use enum constru
7475
### Removed
7576

7677
- implicit assembly loading (you have to explicitly `clr.AddReference` before doing import)
78+
- messages in `PythonException` no longer start with exception type
7779
- support for .NET Framework 4.0-4.6; Mono before 5.4. Python.NET now requires .NET Standard 2.0
7880
(see [the matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-implementation-support))
7981

src/embed_tests/Codecs.cs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -325,56 +325,58 @@ def CanEncode(self, clr_type):
325325

326326
const string TestExceptionMessage = "Hello World!";
327327
[Test]
328-
public void ExceptionEncoded() {
328+
public void ExceptionEncoded()
329+
{
329330
PyObjectConversions.RegisterEncoder(new ValueErrorCodec());
330331
void CallMe() => throw new ValueErrorWrapper(TestExceptionMessage);
331332
var callMeAction = new Action(CallMe);
332-
using (var _ = Py.GIL())
333-
using (var scope = Py.CreateScope())
334-
{
335-
scope.Exec(@"
333+
using var _ = Py.GIL();
334+
using var scope = Py.CreateScope();
335+
scope.Exec(@"
336336
def call(func):
337337
try:
338338
func()
339339
except ValueError as e:
340340
return str(e)
341341
");
342-
var callFunc = scope.Get("call");
343-
string message = callFunc.Invoke(callMeAction.ToPython()).As<string>();
344-
Assert.AreEqual(TestExceptionMessage, message);
345-
}
342+
var callFunc = scope.Get("call");
343+
string message = callFunc.Invoke(callMeAction.ToPython()).As<string>();
344+
Assert.AreEqual(TestExceptionMessage, message);
346345
}
347346

348347
[Test]
349-
public void ExceptionDecoded() {
348+
public void ExceptionDecoded()
349+
{
350350
PyObjectConversions.RegisterDecoder(new ValueErrorCodec());
351-
using (var _ = Py.GIL())
352-
using (var scope = Py.CreateScope())
353-
{
354-
var error = Assert.Throws<ValueErrorWrapper>(()
355-
=> PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')"));
356-
Assert.AreEqual(TestExceptionMessage, error.Message);
357-
}
351+
using var _ = Py.GIL();
352+
using var scope = Py.CreateScope();
353+
var error = Assert.Throws<ValueErrorWrapper>(()
354+
=> PythonEngine.Exec($"raise ValueError('{TestExceptionMessage}')"));
355+
Assert.AreEqual(TestExceptionMessage, error.Message);
358356
}
359357

360-
class ValueErrorWrapper : Exception {
358+
class ValueErrorWrapper : Exception
359+
{
361360
public ValueErrorWrapper(string message) : base(message) { }
362361
}
363362

364-
class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder {
363+
class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder
364+
{
365365
public bool CanDecode(PyObject objectType, Type targetType)
366366
=> this.CanEncode(targetType) && objectType.Equals(PythonEngine.Eval("ValueError"));
367367

368368
public bool CanEncode(Type type) => type == typeof(ValueErrorWrapper)
369369
|| typeof(ValueErrorWrapper).IsSubclassOf(type);
370370

371-
public bool TryDecode<T>(PyObject pyObj, out T value) {
371+
public bool TryDecode<T>(PyObject pyObj, out T value)
372+
{
372373
var message = pyObj.GetAttr("args")[0].As<string>();
373374
value = (T)(object)new ValueErrorWrapper(message);
374375
return true;
375376
}
376377

377-
public PyObject TryEncode(object value) {
378+
public PyObject TryEncode(object value)
379+
{
378380
var error = (ValueErrorWrapper)value;
379381
return PythonEngine.Eval("ValueError").Invoke(error.Message.ToPython());
380382
}

src/embed_tests/TestCallbacks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void TestNoOverloadException() {
2424
using (Py.GIL()) {
2525
dynamic callWith42 = PythonEngine.Eval("lambda f: f([42])");
2626
var error = Assert.Throws<PythonException>(() => callWith42(aFunctionThatCallsIntoPython.ToPython()));
27-
Assert.AreEqual("TypeError", error.PythonTypeName);
27+
Assert.AreEqual("TypeError", error.Type.Name);
2828
string expectedArgTypes = "(<class 'list'>)";
2929
StringAssert.EndsWith(expectedArgTypes, error.Message);
3030
}

src/embed_tests/TestPyFloat.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public void StringBadCtor()
9595

9696
var ex = Assert.Throws<PythonException>(() => a = new PyFloat(i));
9797

98-
StringAssert.StartsWith("ValueError : could not convert string to float", ex.Message);
98+
StringAssert.StartsWith("could not convert string to float", ex.Message);
9999
Assert.IsNull(a);
100100
}
101101

@@ -132,7 +132,7 @@ public void AsFloatBad()
132132
PyFloat a = null;
133133

134134
var ex = Assert.Throws<PythonException>(() => a = PyFloat.AsFloat(s));
135-
StringAssert.StartsWith("ValueError : could not convert string to float", ex.Message);
135+
StringAssert.StartsWith("could not convert string to float", ex.Message);
136136
Assert.IsNull(a);
137137
}
138138
}

src/embed_tests/TestPyInt.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public void TestCtorBadString()
128128

129129
var ex = Assert.Throws<PythonException>(() => a = new PyInt(i));
130130

131-
StringAssert.StartsWith("ValueError : invalid literal for int", ex.Message);
131+
StringAssert.StartsWith("invalid literal for int", ex.Message);
132132
Assert.IsNull(a);
133133
}
134134

@@ -161,7 +161,7 @@ public void TestAsIntBad()
161161
PyInt a = null;
162162

163163
var ex = Assert.Throws<PythonException>(() => a = PyInt.AsInt(s));
164-
StringAssert.StartsWith("ValueError : invalid literal for int", ex.Message);
164+
StringAssert.StartsWith("invalid literal for int", ex.Message);
165165
Assert.IsNull(a);
166166
}
167167

src/embed_tests/TestPyList.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void TestStringAsListType()
4141

4242
var ex = Assert.Throws<PythonException>(() => t = PyList.AsList(i));
4343

44-
Assert.AreEqual("TypeError : 'int' object is not iterable", ex.Message);
44+
Assert.AreEqual("'int' object is not iterable", ex.Message);
4545
Assert.IsNull(t);
4646
}
4747

src/embed_tests/TestPyLong.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public void TestCtorBadString()
144144

145145
var ex = Assert.Throws<PythonException>(() => a = new PyLong(i));
146146

147-
StringAssert.StartsWith("ValueError : invalid literal", ex.Message);
147+
StringAssert.StartsWith("invalid literal", ex.Message);
148148
Assert.IsNull(a);
149149
}
150150

@@ -177,7 +177,7 @@ public void TestAsLongBad()
177177
PyLong a = null;
178178

179179
var ex = Assert.Throws<PythonException>(() => a = PyLong.AsLong(s));
180-
StringAssert.StartsWith("ValueError : invalid literal", ex.Message);
180+
StringAssert.StartsWith("invalid literal", ex.Message);
181181
Assert.IsNull(a);
182182
}
183183

src/embed_tests/TestPyTuple.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public void TestPyTupleInvalidAppend()
104104

105105
var ex = Assert.Throws<PythonException>(() => t.Concat(s));
106106

107-
StringAssert.StartsWith("TypeError : can only concatenate tuple", ex.Message);
107+
StringAssert.StartsWith("can only concatenate tuple", ex.Message);
108108
Assert.AreEqual(0, t.Length());
109109
Assert.IsEmpty(t);
110110
}
@@ -164,7 +164,7 @@ public void TestInvalidAsTuple()
164164

165165
var ex = Assert.Throws<PythonException>(() => t = PyTuple.AsTuple(i));
166166

167-
Assert.AreEqual("TypeError : 'int' object is not iterable", ex.Message);
167+
Assert.AreEqual("'int' object is not iterable", ex.Message);
168168
Assert.IsNull(t);
169169
}
170170
}

src/embed_tests/TestPyType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public void CanCreateHeapType()
3939

4040
using var type = new PyType(spec);
4141
Assert.AreEqual(name, type.GetAttr("__name__").As<string>());
42+
Assert.AreEqual(name, type.Name);
4243
Assert.AreEqual(docStr, type.GetAttr("__doc__").As<string>());
4344
}
4445
}

src/embed_tests/TestPyWith.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def fail(self):
5151
catch (PythonException e)
5252
{
5353
TestContext.Out.WriteLine(e.Message);
54-
Assert.IsTrue(e.Message.Contains("ZeroDivisionError"));
54+
Assert.IsTrue(e.Type.Name == "ZeroDivisionError");
5555
}
5656
}
5757

0 commit comments

Comments
 (0)