diff -r b8ea5e1a5aa2 Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Fri Jun 13 21:28:21 2008 +0200 +++ b/Lib/test/test_exceptions.py Sat Jun 14 02:00:41 2008 +0200 @@ -480,7 +480,12 @@ class ExceptionTests(unittest.TestCase): inner_raising_func() except: raise KeyError - except KeyError: + except KeyError as e: + # We want to test that the except block above got rid of + # the exception raised in inner_raising_func(), but it + # also ends up in the __context__ of the KeyError, so we + # must clear the latter manually for our test to succeed. + e.__context__ = None obj = None obj = wr() self.failUnless(obj is None, "%s" % obj) diff -r b8ea5e1a5aa2 Lib/test/test_raise.py --- a/Lib/test/test_raise.py Fri Jun 13 21:28:21 2008 +0200 +++ b/Lib/test/test_raise.py Sat Jun 14 02:00:41 2008 +0200 @@ -181,32 +181,102 @@ class TestTraceback(unittest.TestCase): self.fail("No exception raised") -# Disabled until context is implemented -# class TestContext(object): -# def test_instance_context_bare_raise(self): -# context = IndexError() -# try: -# try: -# raise context -# except: -# raise OSError() -# except OSError as e: -# self.assertEqual(e.__context__, context) -# else: -# self.fail("No exception raised") -# -# def test_class_context_bare_raise(self): -# context = IndexError -# try: -# try: -# raise context -# except: -# raise OSError() -# except OSError as e: -# self.assertNotEqual(e.__context__, context) -# self.failUnless(isinstance(e.__context__, context)) -# else: -# self.fail("No exception raised") +class TestContext(unittest.TestCase): + def test_instance_context_instance_raise(self): + context = IndexError() + try: + try: + raise context + except: + raise OSError() + except OSError as e: + self.assertEqual(e.__context__, context) + else: + self.fail("No exception raised") + + def test_class_context_instance_raise(self): + context = IndexError + try: + try: + raise context + except: + raise OSError() + except OSError as e: + self.assertNotEqual(e.__context__, context) + self.failUnless(isinstance(e.__context__, context)) + else: + self.fail("No exception raised") + + def test_class_context_class_raise(self): + context = IndexError + try: + try: + raise context + except: + raise OSError + except OSError as e: + self.assertNotEqual(e.__context__, context) + self.failUnless(isinstance(e.__context__, context)) + else: + self.fail("No exception raised") + + def test_c_exception_context(self): + try: + try: + 1/0 + except: + raise OSError + except OSError as e: + self.failUnless(isinstance(e.__context__, ZeroDivisionError)) + else: + self.fail("No exception raised") + + def test_c_exception_raise(self): + try: + try: + 1/0 + except: + xyzzy + except NameError as e: + self.failUnless(isinstance(e.__context__, ZeroDivisionError)) + else: + self.fail("No exception raised") + + def test_noraise_finally(self): + try: + try: + pass + finally: + raise OSError + except OSError as e: + self.failUnless(e.__context__ is None) + else: + self.fail("No exception raised") + + def test_raise_finally(self): + try: + try: + 1/0 + finally: + raise OSError + except OSError as e: + self.failUnless(isinstance(e.__context__, ZeroDivisionError)) + else: + self.fail("No exception raised") + + def test_context_manager(self): + class ContextManager: + def __enter__(self): + pass + def __exit__(self, t, v, tb): + xyzzy + try: + with ContextManager(): + 1/0 + except NameError as e: + self.failUnless(isinstance(e.__context__, ZeroDivisionError)) + else: + self.fail("No exception raised") class TestRemovedFunctionality(unittest.TestCase): diff -r b8ea5e1a5aa2 Python/ceval.c --- a/Python/ceval.c Fri Jun 13 21:28:21 2008 +0200 +++ b/Python/ceval.c Sat Jun 14 02:00:41 2008 +0200 @@ -2817,11 +2817,12 @@ static enum why_code static enum why_code do_raise(PyObject *exc, PyObject *cause) { - PyObject *type = NULL, *value = NULL, *tb = NULL; + PyObject *type = NULL, *value = NULL; if (exc == NULL) { /* Reraise */ PyThreadState *tstate = PyThreadState_GET(); + PyObject *tb; type = tstate->exc_type; value = tstate->exc_value; tb = tstate->exc_traceback; @@ -2862,7 +2863,6 @@ do_raise(PyObject *exc, PyObject *cause) goto raise_error; } - tb = PyException_GetTraceback(value); if (cause) { PyObject *fixed_cause; if (PyExceptionClass_Check(cause)) { @@ -2883,13 +2883,15 @@ do_raise(PyObject *exc, PyObject *cause) PyException_SetCause(value, fixed_cause); } - PyErr_Restore(type, value, tb); + PyErr_SetObject(type, value); + /* PyErr_SetObject incref's its arguments */ + Py_XDECREF(value); + Py_XDECREF(type); return WHY_EXCEPTION; raise_error: Py_XDECREF(value); Py_XDECREF(type); - Py_XDECREF(tb); Py_XDECREF(cause); return WHY_EXCEPTION; } diff -r b8ea5e1a5aa2 Python/errors.c --- a/Python/errors.c Fri Jun 13 21:28:21 2008 +0200 +++ b/Python/errors.c Sat Jun 14 02:00:42 2008 +0200 @@ -52,6 +52,9 @@ void void PyErr_SetObject(PyObject *exception, PyObject *value) { + PyThreadState *tstate = PyThreadState_GET(); + PyObject *tb = NULL; + if (exception != NULL && !PyExceptionClass_Check(exception)) { PyErr_Format(PyExc_SystemError, @@ -59,9 +62,35 @@ PyErr_SetObject(PyObject *exception, PyO exception); return; } + Py_XINCREF(value); + if (tstate->exc_value != NULL && tstate->exc_value != Py_None) { + /* Implicit exception chaining */ + if (value == NULL || !PyExceptionInstance_Check(value)) { + /* We must normalize the value right now */ + PyObject *args, *fixed_value; + if (value == NULL || value == Py_None) + args = PyTuple_New(0); + else if (PyTuple_Check(value)) { + Py_INCREF(value); + args = value; + } + else + args = PyTuple_Pack(1, value); + fixed_value = args ? + PyEval_CallObject(exception, args) : NULL; + Py_XDECREF(args); + Py_XDECREF(value); + if (fixed_value == NULL) + return; + value = fixed_value; + } + Py_INCREF(tstate->exc_value); + PyException_SetContext(value, tstate->exc_value); + } + if (value != NULL && PyExceptionInstance_Check(value)) + tb = PyException_GetTraceback(value); Py_XINCREF(exception); - Py_XINCREF(value); - PyErr_Restore(exception, value, (PyObject *)NULL); + PyErr_Restore(exception, value, tb); } void