diff --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py index 4537e9afcfb..5a63b868500 100644 --- a/Lib/test/test_raise.py +++ b/Lib/test/test_raise.py @@ -324,6 +324,30 @@ def f(): f() + def test_3611(self): + # A re-raised exception in a __del__ caused the __context__ + # to be cleared + class C: + def __del__(self): + try: + 1/0 + except: + raise + + def f(): + x = C() + try: + try: + x.x + except AttributeError: + del x + raise TypeError + except Exception as e: + self.assertNotEqual(e.__context__, None) + self.assert_(isinstance(e.__context__, AttributeError)) + + with support.captured_output("stderr"): + f() class TestRemovedFunctionality(unittest.TestCase): def test_tuples(self): diff --git a/Misc/NEWS b/Misc/NEWS index 3927f9b86b1..5372f0b9a92 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.0 release candidate 1 Core and Builtins ----------------- +- Issue #3611: An exception __context__ could be cleared in a complex pattern + involving a __del__ method re-raising an exception. + - Issue #2534: speed up isinstance() and issubclass() by 50-70%, so as to match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__ mechanism. In the process, fix a bug where isinstance() and issubclass(), diff --git a/Python/ceval.c b/Python/ceval.c index 3af0cef1a61..42df3cbb131 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2453,11 +2453,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (b->b_type == EXCEPT_HANDLER) { UNWIND_EXCEPT_HANDLER(b); - if (why == WHY_EXCEPTION && !throwflag) { - Py_CLEAR(tstate->exc_type); - Py_CLEAR(tstate->exc_value); - Py_CLEAR(tstate->exc_traceback); - } continue; } UNWIND_BLOCK(b); diff --git a/Python/errors.c b/Python/errors.c index d7aac6d5041..63353881b00 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -53,6 +53,7 @@ void PyErr_SetObject(PyObject *exception, PyObject *value) { PyThreadState *tstate = PyThreadState_GET(); + PyObject *exc_value; PyObject *tb = NULL; if (exception != NULL && @@ -63,8 +64,10 @@ PyErr_SetObject(PyObject *exception, PyObject *value) return; } Py_XINCREF(value); - if (tstate->exc_value != NULL && tstate->exc_value != Py_None) { + exc_value = tstate->exc_value; + if (exc_value != NULL && exc_value != Py_None) { /* Implicit exception chaining */ + Py_INCREF(exc_value); if (value == NULL || !PyExceptionInstance_Check(value)) { /* We must normalize the value right now */ PyObject *args, *fixed_value; @@ -88,8 +91,8 @@ PyErr_SetObject(PyObject *exception, PyObject *value) This is O(chain length) but context chains are usually very short. Sensitive readers may try to inline the call to PyException_GetContext. */ - if (tstate->exc_value != value) { - PyObject *o = tstate->exc_value, *context; + if (exc_value != value) { + PyObject *o = exc_value, *context; while ((context = PyException_GetContext(o))) { Py_DECREF(context); if (context == value) { @@ -98,8 +101,9 @@ PyErr_SetObject(PyObject *exception, PyObject *value) } o = context; } - Py_INCREF(tstate->exc_value); - PyException_SetContext(value, tstate->exc_value); + PyException_SetContext(value, exc_value); + } else { + Py_DECREF(exc_value); } } if (value != NULL && PyExceptionInstance_Check(value))