diff --git a/Examples/test-suite/python/Makefile.in b/Examples/test-suite/python/Makefile.in index 47535f569..096e624ac 100644 --- a/Examples/test-suite/python/Makefile.in +++ b/Examples/test-suite/python/Makefile.in @@ -58,6 +58,7 @@ CPP_TEST_CASES += \ primitive_types \ python_abstractbase \ python_append \ + python_destructor_exception \ python_director \ python_docstring \ python_nondynamic \ diff --git a/Examples/test-suite/python/python_destructor_exception_runme.py b/Examples/test-suite/python/python_destructor_exception_runme.py new file mode 100644 index 000000000..a19b48633 --- /dev/null +++ b/Examples/test-suite/python/python_destructor_exception_runme.py @@ -0,0 +1,14 @@ +import python_destructor_exception +from StringIO import StringIO +import sys + +#buffer = StringIO() +#sys.stderr = buffer + +attributeErrorOccurred = False +try: + python_destructor_exception.ClassWithThrowingDestructor().GetBlah() +except AttributeError, e: + attributeErrorOccurred = True + +assert attributeErrorOccurred diff --git a/Examples/test-suite/python_destructor_exception.i b/Examples/test-suite/python_destructor_exception.i new file mode 100644 index 000000000..4d2745ac8 --- /dev/null +++ b/Examples/test-suite/python_destructor_exception.i @@ -0,0 +1,17 @@ +/* File : example.i */ +%module python_destructor_exception +%include exception.i + +%exception ClassWithThrowingDestructor::~ClassWithThrowingDestructor() +{ + $action + SWIG_exception(SWIG_RuntimeError, "I am the ClassWithThrowingDestructor dtor doing bad things"); +} + +%inline %{ +class ClassWithThrowingDestructor +{ +}; + +%} + diff --git a/Lib/python/pyrun.swg b/Lib/python/pyrun.swg index ba9d6ecca..f25d5ab9c 100644 --- a/Lib/python/pyrun.swg +++ b/Lib/python/pyrun.swg @@ -536,23 +536,32 @@ SwigPyObject_dealloc(PyObject *v) if (destroy) { /* destroy is always a VARARGS method */ PyObject *res; + + /* PyObject_CallFunction() has the potential to silently drop + the active active exception. In cases of unnamed temporary + variable or where we just finished iterating over a generator + StopIteration will be active right now, and this needs to + remain true upon return from SwigPyObject_dealloc. So save + and restore. */ + + PyObject *val = NULL, *type = NULL, *tb = NULL; + PyErr_Fetch(&val, &type, &tb); + if (data->delargs) { /* we need to create a temporary object to carry the destroy operation */ PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); - /* PyObject_CallFunction() has the potential to silently drop the active - active exception. In cases where we just finished iterating over a - generator StopIteration will be active right now, and this needs to - remain true upon return from SwigPyObject_dealloc. So save and restore. */ - PyObject *val = NULL, *type = NULL, *tb = NULL; - PyErr_Fetch(&val, &type, &tb); res = SWIG_Python_CallFunctor(destroy, tmp); - PyErr_Restore(val, type, tb); Py_DECREF(tmp); } else { PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); PyObject *mself = PyCFunction_GET_SELF(destroy); res = ((*meth)(mself, v)); } + if (!res) + PyErr_WriteUnraisable(destroy); + + PyErr_Restore(val, type, tb); + Py_XDECREF(res); } #if !defined(SWIG_PYTHON_SILENT_MEMLEAK)