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):
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):
......
......@@ -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(),
......
......@@ -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);
......
......@@ -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))
......
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