]> granicus.if.org Git - python/commitdiff
Issue 3611: in some cases (a __del__ re-raising an exception, when called from inside
authorAmaury Forgeot d'Arc <amauryfa@gmail.com>
Fri, 29 Aug 2008 07:13:32 +0000 (07:13 +0000)
committerAmaury Forgeot d'Arc <amauryfa@gmail.com>
Fri, 29 Aug 2008 07:13:32 +0000 (07:13 +0000)
an 'except' clause), the exception __context__ would be reset to None.
This crases the interpreter if this precisely happens inside PyErr_SetObject.

- now the __context__ is properly preserved
- in any case, PyErr_SetObject now saves the current exc_value in a local variable, to
avoid such crashes in the future.

Reviewer: Antoine Pitrou.

Lib/test/test_raise.py
Misc/NEWS
Python/ceval.c
Python/errors.c

index 4537e9afcfb3fc5267316afdd7c09b93f45da9dd..5a63b868500f1dc9d58d2fc08dd3fcf9b7f1a724 100644 (file)
@@ -324,6 +324,30 @@ class TestContext(unittest.TestCase):
 
         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):
index 3927f9b86b193f8d3c6fb503129ac6eb54901c7f..5372f0b9a926f13c29c176a520b056d7d84e1472 100644 (file)
--- 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(),
index 3af0cef1a614db953164a94e835ddb5746f3ab10..42df3cbb1315dd5ed37cc1a03f658053630b818a 100644 (file)
@@ -2453,11 +2453,6 @@ fast_block_end:
 
                        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);
index d7aac6d504182ac15bfe8127827d9070faa76808..63353881b004143373632f723315fb755ce9043f 100644 (file)
@@ -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))