[Python-3000-checkins] r66056 - in python/branches/py3k: Lib/test/test_raise.py Misc/NEWS Python/ceval.c Python/errors.c

amaury.forgeotdarc python-3000-checkins at python.org
Fri Aug 29 09:13:33 CEST 2008


Author: amaury.forgeotdarc
Date: Fri Aug 29 09:13:32 2008
New Revision: 66056

Log:
Issue 3611: in some cases (a __del__ re-raising an exception, when called from inside
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.


Modified:
   python/branches/py3k/Lib/test/test_raise.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Python/ceval.c
   python/branches/py3k/Python/errors.c

Modified: python/branches/py3k/Lib/test/test_raise.py
==============================================================================
--- python/branches/py3k/Lib/test/test_raise.py	(original)
+++ python/branches/py3k/Lib/test/test_raise.py	Fri Aug 29 09:13:32 2008
@@ -324,6 +324,30 @@
 
         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):

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri Aug 29 09:13:32 2008
@@ -12,6 +12,9 @@
 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(),

Modified: python/branches/py3k/Python/ceval.c
==============================================================================
--- python/branches/py3k/Python/ceval.c	(original)
+++ python/branches/py3k/Python/ceval.c	Fri Aug 29 09:13:32 2008
@@ -2453,11 +2453,6 @@
 
 			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);

Modified: python/branches/py3k/Python/errors.c
==============================================================================
--- python/branches/py3k/Python/errors.c	(original)
+++ python/branches/py3k/Python/errors.c	Fri Aug 29 09:13:32 2008
@@ -53,6 +53,7 @@
 PyErr_SetObject(PyObject *exception, PyObject *value)
 {
 	PyThreadState *tstate = PyThreadState_GET();
+	PyObject *exc_value;
 	PyObject *tb = NULL;
 
 	if (exception != NULL &&
@@ -63,8 +64,10 @@
 		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 @@
 		   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 @@
 				}
 				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))


More information about the Python-3000-checkins mailing list