diff --git a/CHANGELOG.md b/CHANGELOG.md index ef9358793..298f3b443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. ### Changed +- Reattach python exception traceback information (#545) + ### Fixed - Fixed secondary PythonEngine.Initialize call, all sensitive static variables now reseted. @@ -697,4 +699,4 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. [i131]: https://github.com/pythonnet/pythonnet/issues/131 [p531]: https://github.com/pythonnet/pythonnet/pull/531 [i755]: https://github.com/pythonnet/pythonnet/pull/755 -[p534]: https://github.com/pythonnet/pythonnet/pull/534 \ No newline at end of file +[p534]: https://github.com/pythonnet/pythonnet/pull/534 diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 743b5416f..8bed0abfd 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -256,7 +256,10 @@ public static void SetError(Exception e) var pe = e as PythonException; if (pe != null) { - Runtime.PyErr_SetObject(pe.PyType, pe.PyValue); + Runtime.XIncref(pe.PyType); + Runtime.XIncref(pe.PyValue); + Runtime.XIncref(pe.PyTB); + Runtime.PyErr_Restore(pe.PyType, pe.PyValue, pe.PyTB); return; } diff --git a/src/tests/test_subclass.py b/src/tests/test_subclass.py index 43d013c7c..ab440d429 100644 --- a/src/tests/test_subclass.py +++ b/src/tests/test_subclass.py @@ -128,6 +128,39 @@ def test_derived_class(): assert id(x) == id(ob) +def test_derived_traceback(): + """Test python exception traceback in class derived from managed base""" + class DerivedClass(SubClassTest): + __namespace__ = "Python.Test.traceback" + + def foo(self): + print (xyzname) + return None + + import sys,traceback + ob = DerivedClass() + + # direct call + try: + ob.foo() + assert False + except: + e = sys.exc_info() + assert "xyzname" in str(e[1]) + location = traceback.extract_tb(e[2])[-1] + assert location[2] == "foo" + + # call through managed code + try: + FunctionsTest.test_foo(ob) + assert False + except: + e = sys.exc_info() + assert "xyzname" in str(e[1]) + location = traceback.extract_tb(e[2])[-1] + assert location[2] == "foo" + + def test_create_instance(): """Test derived instances can be created from managed code""" DerivedClass = derived_class_fixture(test_create_instance.__name__)