Skip to content

Commit 1afae4c

Browse files
authored
Fix exception string (#1360)
Fix exception stacktrace formatting Ensure that the stacktrace list is joined into a single string and adjust the formatting slightly such that the .NET stacktrace is separated from the one from Python. The Python traceback is also reversed to match the order used in .NET. Fixes #1359.
1 parent 9e5887c commit 1afae4c

File tree

3 files changed

+51
-31
lines changed

3 files changed

+51
-31
lines changed

CHANGELOG.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,23 @@ or the DLL must be loaded in advance. This must be done before calling any other
3939

4040
### Fixed
4141

42-
- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash
43-
- Fix incorrect dereference in params array handling
44-
- Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097])
45-
- Fix `object[]` parameters taking precedence when should not in overload resolution
46-
- Fixed a bug where all .NET class instances were considered Iterable
47-
- Fix incorrect choice of method to invoke when using keyword arguments.
48-
- Fix non-delegate types incorrectly appearing as callable.
49-
- Indexers can now be used with interface objects
50-
- Fixed a bug where indexers could not be used if they were inherited
51-
- Made it possible to use `__len__` also on `ICollection<>` interface objects
52-
- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions
53-
- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects
54-
- Fixed objects returned by enumerating `PyObject` being disposed too soon
55-
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException ([#1325][i1325])
56-
- `import` may now raise errors with more detail than "No module named X"
57-
- Providing an invalid type parameter to a generic type or method produces a helpful Python error
42+
- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash
43+
- Fix incorrect dereference in params array handling
44+
- Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097])
45+
- Fix `object[]` parameters taking precedence when should not in overload resolution
46+
- Fixed a bug where all .NET class instances were considered Iterable
47+
- Fix incorrect choice of method to invoke when using keyword arguments.
48+
- Fix non-delegate types incorrectly appearing as callable.
49+
- Indexers can now be used with interface objects
50+
- Fixed a bug where indexers could not be used if they were inherited
51+
- Made it possible to use `__len__` also on `ICollection<>` interface objects
52+
- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions
53+
- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects
54+
- Fixed objects returned by enumerating `PyObject` being disposed too soon
55+
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException
56+
- `import` may now raise errors with more detail than "No module named X"
57+
- Exception stacktraces on `PythonException.StackTrace` are now properly formatted
58+
- Providing an invalid type parameter to a generic type or method produces a helpful Python error
5859

5960
### Removed
6061

src/embed_tests/TestPythonException.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,21 @@ public void TestPythonExceptionFormat()
6565
}
6666
catch (PythonException ex)
6767
{
68-
Assert.That(ex.Format(), Does.Contain("Traceback").And.Contains("(most recent call last):").And.Contains("ValueError: Error!"));
68+
// Console.WriteLine($"Format: {ex.Format()}");
69+
// Console.WriteLine($"Stacktrace: {ex.StackTrace}");
70+
Assert.That(
71+
ex.Format(),
72+
Does.Contain("Traceback")
73+
.And.Contains("(most recent call last):")
74+
.And.Contains("ValueError: Error!")
75+
);
76+
77+
// Check that the stacktrace is properly formatted
78+
Assert.That(
79+
ex.StackTrace,
80+
Does.Not.StartWith("[")
81+
.And.Not.Contain("\\n")
82+
);
6983
}
7084
}
7185

src/runtime/pythonexception.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Runtime.CompilerServices;
34
using System.Text;
45

@@ -13,9 +14,9 @@ public class PythonException : System.Exception, IDisposable
1314
private IntPtr _pyType = IntPtr.Zero;
1415
private IntPtr _pyValue = IntPtr.Zero;
1516
private IntPtr _pyTB = IntPtr.Zero;
16-
private string _tb = "";
17-
private string _message = "";
18-
private string _pythonTypeName = "";
17+
private readonly string _tb = "";
18+
private readonly string _message = "";
19+
private readonly string _pythonTypeName = "";
1920
private bool disposed = false;
2021
private bool _finalized = false;
2122

@@ -44,16 +45,23 @@ public PythonException()
4445
}
4546
_message = type + " : " + message;
4647
}
48+
4749
if (_pyTB != IntPtr.Zero)
4850
{
49-
using (PyObject tb_module = PythonEngine.ImportModule("traceback"))
50-
{
51-
Runtime.XIncref(_pyTB);
52-
using (var pyTB = new PyObject(_pyTB))
53-
{
54-
_tb = tb_module.InvokeMethod("format_tb", pyTB).ToString();
55-
}
51+
using PyObject tb_module = PythonEngine.ImportModule("traceback");
52+
53+
Runtime.XIncref(_pyTB);
54+
using var pyTB = new PyObject(_pyTB);
55+
56+
using var tbList = tb_module.InvokeMethod("format_tb", pyTB);
57+
58+
var sb = new StringBuilder();
59+
// Reverse Python's traceback list to match the order used in C#
60+
// stacktraces
61+
foreach (var line in tbList.Reverse()) {
62+
sb.Append(line.ToString());
5663
}
64+
_tb = sb.ToString();
5765
}
5866
PythonEngine.ReleaseLock(gs);
5967
}
@@ -136,10 +144,7 @@ public override string Message
136144
/// <remarks>
137145
/// A string representing the python exception stack trace.
138146
/// </remarks>
139-
public override string StackTrace
140-
{
141-
get { return _tb + base.StackTrace; }
142-
}
147+
public override string StackTrace => $"{_tb}===\n{base.StackTrace}";
143148

144149
/// <summary>
145150
/// Python error type name.

0 commit comments

Comments
 (0)