Kaydet (Commit) db26f7c1 authored tarafından Amaury Forgeot d'Arc's avatar Amaury Forgeot d'Arc

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.
üst 4f3c5616
...@@ -324,6 +324,30 @@ class TestContext(unittest.TestCase): ...@@ -324,6 +324,30 @@ class TestContext(unittest.TestCase):
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): class TestRemovedFunctionality(unittest.TestCase):
def test_tuples(self): def test_tuples(self):
......
...@@ -12,6 +12,9 @@ What's New in Python 3.0 release candidate 1 ...@@ -12,6 +12,9 @@ What's New in Python 3.0 release candidate 1
Core and Builtins 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 - Issue #2534: speed up isinstance() and issubclass() by 50-70%, so as to
match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__ match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__
mechanism. In the process, fix a bug where isinstance() and issubclass(), mechanism. In the process, fix a bug where isinstance() and issubclass(),
......
...@@ -2453,11 +2453,6 @@ fast_block_end: ...@@ -2453,11 +2453,6 @@ fast_block_end:
if (b->b_type == EXCEPT_HANDLER) { if (b->b_type == EXCEPT_HANDLER) {
UNWIND_EXCEPT_HANDLER(b); 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; continue;
} }
UNWIND_BLOCK(b); UNWIND_BLOCK(b);
......
...@@ -53,6 +53,7 @@ void ...@@ -53,6 +53,7 @@ void
PyErr_SetObject(PyObject *exception, PyObject *value) PyErr_SetObject(PyObject *exception, PyObject *value)
{ {
PyThreadState *tstate = PyThreadState_GET(); PyThreadState *tstate = PyThreadState_GET();
PyObject *exc_value;
PyObject *tb = NULL; PyObject *tb = NULL;
if (exception != NULL && if (exception != NULL &&
...@@ -63,8 +64,10 @@ PyErr_SetObject(PyObject *exception, PyObject *value) ...@@ -63,8 +64,10 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
return; return;
} }
Py_XINCREF(value); 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 */ /* Implicit exception chaining */
Py_INCREF(exc_value);
if (value == NULL || !PyExceptionInstance_Check(value)) { if (value == NULL || !PyExceptionInstance_Check(value)) {
/* We must normalize the value right now */ /* We must normalize the value right now */
PyObject *args, *fixed_value; PyObject *args, *fixed_value;
...@@ -88,8 +91,8 @@ PyErr_SetObject(PyObject *exception, PyObject *value) ...@@ -88,8 +91,8 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
This is O(chain length) but context chains are This is O(chain length) but context chains are
usually very short. Sensitive readers may try usually very short. Sensitive readers may try
to inline the call to PyException_GetContext. */ to inline the call to PyException_GetContext. */
if (tstate->exc_value != value) { if (exc_value != value) {
PyObject *o = tstate->exc_value, *context; PyObject *o = exc_value, *context;
while ((context = PyException_GetContext(o))) { while ((context = PyException_GetContext(o))) {
Py_DECREF(context); Py_DECREF(context);
if (context == value) { if (context == value) {
...@@ -98,8 +101,9 @@ PyErr_SetObject(PyObject *exception, PyObject *value) ...@@ -98,8 +101,9 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
} }
o = context; o = context;
} }
Py_INCREF(tstate->exc_value); PyException_SetContext(value, exc_value);
PyException_SetContext(value, tstate->exc_value); } else {
Py_DECREF(exc_value);
} }
} }
if (value != NULL && PyExceptionInstance_Check(value)) if (value != NULL && PyExceptionInstance_Check(value))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment