diff options
-rw-r--r-- | Lib/test/test_exceptions.py | 7 | ||||
-rw-r--r-- | Lib/test/test_raise.py | 122 | ||||
-rw-r--r-- | Python/ceval.c | 10 | ||||
-rw-r--r-- | Python/errors.c | 33 |
4 files changed, 139 insertions, 33 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 9068554..55a57ba 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -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 --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py index 5f0070e..3072c14 100644 --- a/Lib/test/test_raise.py +++ b/Lib/test/test_raise.py @@ -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 --git a/Python/ceval.c b/Python/ceval.c index bcb15e7..6aaa52d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2817,11 +2817,12 @@ fail: /* Jump here from prelude on failure */ 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 --git a/Python/errors.c b/Python/errors.c index b765b03..ac64b6a 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -52,6 +52,9 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) 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, PyObject *value) exception); return; } - Py_XINCREF(exception); Py_XINCREF(value); - PyErr_Restore(exception, value, (PyObject *)NULL); + 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); + PyErr_Restore(exception, value, tb); } void |