From 1ab234a2ab4316d2398a87f5199de9228022ef05 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 21 Jan 2021 08:42:41 +0100 Subject: [PATCH 1/4] Check the stacktrace format in the unit test --- src/embed_tests/TestPythonException.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/embed_tests/TestPythonException.cs b/src/embed_tests/TestPythonException.cs index e51da4d4f..31addfba1 100644 --- a/src/embed_tests/TestPythonException.cs +++ b/src/embed_tests/TestPythonException.cs @@ -65,7 +65,21 @@ public void TestPythonExceptionFormat() } catch (PythonException ex) { - Assert.That(ex.Format(), Does.Contain("Traceback").And.Contains("(most recent call last):").And.Contains("ValueError: Error!")); + // Console.WriteLine($"Format: {ex.Format()}"); + // Console.WriteLine($"Stacktrace: {ex.StackTrace}"); + Assert.That( + ex.Format(), + Does.Contain("Traceback") + .And.Contains("(most recent call last):") + .And.Contains("ValueError: Error!") + ); + + // Check that the stacktrace is properly formatted + Assert.That( + ex.StackTrace, + Does.Not.StartWith("[") + .And.Not.Contain("\\n") + ); } } From 1819ba9836eeb36c88ffafa76e0c710a251d11f2 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 21 Jan 2021 08:46:14 +0100 Subject: [PATCH 2/4] 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. Fixes #1359. --- src/runtime/pythonexception.cs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index ad4d79960..cb4aca17b 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -13,9 +13,9 @@ public class PythonException : System.Exception, IDisposable private IntPtr _pyType = IntPtr.Zero; private IntPtr _pyValue = IntPtr.Zero; private IntPtr _pyTB = IntPtr.Zero; - private string _tb = ""; - private string _message = ""; - private string _pythonTypeName = ""; + private readonly string _tb = ""; + private readonly string _message = ""; + private readonly string _pythonTypeName = ""; private bool disposed = false; private bool _finalized = false; @@ -46,14 +46,18 @@ public PythonException() } if (_pyTB != IntPtr.Zero) { - using (PyObject tb_module = PythonEngine.ImportModule("traceback")) - { - Runtime.XIncref(_pyTB); - using (var pyTB = new PyObject(_pyTB)) - { - _tb = tb_module.InvokeMethod("format_tb", pyTB).ToString(); - } + using PyObject tb_module = PythonEngine.ImportModule("traceback"); + + Runtime.XIncref(_pyTB); + using var pyTB = new PyObject(_pyTB); + + using var tbList = tb_module.InvokeMethod("format_tb", pyTB); + + var sb = new StringBuilder(); + foreach (var line in tbList) { + sb.Append(line.ToString()); } + _tb = sb.ToString(); } PythonEngine.ReleaseLock(gs); } @@ -136,10 +140,7 @@ public override string Message /// /// A string representing the python exception stack trace. /// - public override string StackTrace - { - get { return _tb + base.StackTrace; } - } + public override string StackTrace => $"{_tb}===\n{base.StackTrace}"; /// /// Python error type name. From 5ae34ba1fc77fdb65a0d72382724497d2f2da573 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 21 Jan 2021 08:52:28 +0100 Subject: [PATCH 3/4] Add CHANGELOG entry --- CHANGELOG.md | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d64f3cdef..5f2e544df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,22 +39,23 @@ or the DLL must be loaded in advance. This must be done before calling any other ### Fixed -- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash -- Fix incorrect dereference in params array handling -- Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097]) -- Fix `object[]` parameters taking precedence when should not in overload resolution -- Fixed a bug where all .NET class instances were considered Iterable -- Fix incorrect choice of method to invoke when using keyword arguments. -- Fix non-delegate types incorrectly appearing as callable. -- Indexers can now be used with interface objects -- Fixed a bug where indexers could not be used if they were inherited -- Made it possible to use `__len__` also on `ICollection<>` interface objects -- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions -- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects -- Fixed objects returned by enumerating `PyObject` being disposed too soon -- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException ([#1325][i1325]) -- `import` may now raise errors with more detail than "No module named X" -- Providing an invalid type parameter to a generic type or method produces a helpful Python error +- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash +- Fix incorrect dereference in params array handling +- Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097]) +- Fix `object[]` parameters taking precedence when should not in overload resolution +- Fixed a bug where all .NET class instances were considered Iterable +- Fix incorrect choice of method to invoke when using keyword arguments. +- Fix non-delegate types incorrectly appearing as callable. +- Indexers can now be used with interface objects +- Fixed a bug where indexers could not be used if they were inherited +- Made it possible to use `__len__` also on `ICollection<>` interface objects +- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions +- Made it possible to call `ToString`, `GetHashCode`, and `GetType` on inteface objects +- Fixed objects returned by enumerating `PyObject` being disposed too soon +- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException +- `import` may now raise errors with more detail than "No module named X" +- Exception stacktraces on `PythonException.StackTrace` are now properly formatted +- Providing an invalid type parameter to a generic type or method produces a helpful Python error ### Removed From ce61a6f3b4c014b3cc62eeffac0f37b989a0b8a6 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 29 Jan 2021 07:45:37 +0100 Subject: [PATCH 4/4] Reverse Python traceback to match C# stacktrace order --- src/runtime/pythonexception.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/runtime/pythonexception.cs b/src/runtime/pythonexception.cs index cb4aca17b..7dd4f0811 100644 --- a/src/runtime/pythonexception.cs +++ b/src/runtime/pythonexception.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -44,6 +45,7 @@ public PythonException() } _message = type + " : " + message; } + if (_pyTB != IntPtr.Zero) { using PyObject tb_module = PythonEngine.ImportModule("traceback"); @@ -54,7 +56,9 @@ public PythonException() using var tbList = tb_module.InvokeMethod("format_tb", pyTB); var sb = new StringBuilder(); - foreach (var line in tbList) { + // Reverse Python's traceback list to match the order used in C# + // stacktraces + foreach (var line in tbList.Reverse()) { sb.Append(line.ToString()); } _tb = sb.ToString();