diff -r 31784350f849 Doc/c-api/exceptions.rst --- a/Doc/c-api/exceptions.rst Wed Feb 22 21:22:20 2012 +0100 +++ b/Doc/c-api/exceptions.rst Thu Feb 23 16:44:52 2012 +0100 @@ -129,6 +129,37 @@ exception state. +.. c:function:: void PyErr_GetExcInfo(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) + + Retrieve the exception info, as known from ``sys.exc_info()``. This refers + to an exception that was already caught, not to an exception that was + freshly raised. Returns new references for the three objects, any of which + may be *NULL*. Does not modify the exception info state. + + .. note:: + + This function is not normally used by code that wants to handle exceptions. + Rather, it can be used when code needs to save and restore the exception + state temporarily. Use :c:func:`PyErr_SetExcInfo` to restore or clear the + exception state. + + +.. c:function:: void PyErr_SetExcInfo(PyObject *type, PyObject *value, PyObject *traceback) + + Set the exception info, as known from ``sys.exc_info()``. This refers + to an exception that was already caught, not to an exception that was + freshly raised. This function steals the references of the arguments. + To clear the exception state, pass *NULL* for all three arguments. + For general rules about the three arguments, see :c:func:`PyErr_Restore`. + + .. note:: + + This function is not normally used by code that wants to handle exceptions. + Rather, it can be used when code needs to save and restore the exception + state temporarily. Use :c:func:`PyErr_GetExcInfo` to read the exception + state. + + .. c:function:: void PyErr_SetString(PyObject *type, const char *message) This is the most common way to set the error indicator. The first argument diff -r 31784350f849 Include/pyerrors.h --- a/Include/pyerrors.h Wed Feb 22 21:22:20 2012 +0100 +++ b/Include/pyerrors.h Thu Feb 23 16:44:52 2012 +0100 @@ -75,6 +75,8 @@ PyAPI_FUNC(void) PyErr_Clear(void); PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(void) PyErr_GetExcInfo(PyObject **, PyObject **, PyObject **); +PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *); #if defined(__clang__) || \ (defined(__GNUC__) && \ diff -r 31784350f849 Lib/test/test_capi.py --- a/Lib/test/test_capi.py Wed Feb 22 21:22:20 2012 +0100 +++ b/Lib/test/test_capi.py Thu Feb 23 16:44:52 2012 +0100 @@ -55,6 +55,29 @@ def test_memoryview_from_NULL_pointer(self): self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer) + def test_exc_info(self): + raised_exception = ValueError("5") + new_exc = TypeError("TEST") + try: + raise raised_exception + except ValueError as e: + tb = e.__traceback__ + orig_sys_exc_info = sys.exc_info() + orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None) + new_sys_exc_info = sys.exc_info() + new_exc_info = _testcapi.set_exc_info(*orig_exc_info) + reset_sys_exc_info = sys.exc_info() + + self.assertEqual(orig_exc_info[1], e) + + self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb)) + self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info) + self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info) + self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None)) + self.assertSequenceEqual(new_sys_exc_info, new_exc_info) + else: + self.assertTrue(False) + @unittest.skipUnless(threading, 'Threading required for this test.') class TestPendingCalls(unittest.TestCase): diff -r 31784350f849 Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Wed Feb 22 21:22:20 2012 +0100 +++ b/Modules/_testcapimodule.c Thu Feb 23 16:44:52 2012 +0100 @@ -1712,6 +1712,29 @@ return NULL; } +static PyObject * +test_set_exc_info(PyObject *self, PyObject *args) +{ + PyObject *orig_exc; + PyObject *new_type, *new_value, *new_tb; + PyObject *type, *value, *tb; + if (!PyArg_ParseTuple(args, "OOO:test_set_exc_info", + &new_type, &new_value, &new_tb)) + return NULL; + + PyErr_GetExcInfo(&type, &value, &tb); + + Py_INCREF(new_type); + Py_INCREF(new_value); + Py_INCREF(new_tb); + PyErr_SetExcInfo(new_type, new_value, new_tb); + + orig_exc = PyTuple_Pack(3, type ? type : Py_None, value ? value : Py_None, tb ? tb : Py_None); + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(tb); + return orig_exc; +} static int test_run_counter = 0; @@ -2494,6 +2517,7 @@ #endif {"traceback_print", traceback_print, METH_VARARGS}, {"exception_print", exception_print, METH_VARARGS}, + {"set_exc_info", test_set_exc_info, METH_VARARGS}, {"argparsing", argparsing, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS}, {"make_exception_with_doc", (PyCFunction)make_exception_with_doc, diff -r 31784350f849 Python/errors.c --- a/Python/errors.c Wed Feb 22 21:22:20 2012 +0100 +++ b/Python/errors.c Thu Feb 23 16:44:52 2012 +0100 @@ -320,6 +320,39 @@ PyErr_Restore(NULL, NULL, NULL); } +void +PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback) +{ + PyThreadState *tstate = PyThreadState_GET(); + + *p_type = tstate->exc_type; + *p_value = tstate->exc_value; + *p_traceback = tstate->exc_traceback; + + Py_XINCREF(*p_type); + Py_XINCREF(*p_value); + Py_XINCREF(*p_traceback); +} + +void +PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback) +{ + PyObject *oldtype, *oldvalue, *oldtraceback; + PyThreadState *tstate = PyThreadState_GET(); + + oldtype = tstate->exc_type; + oldvalue = tstate->exc_value; + oldtraceback = tstate->exc_traceback; + + tstate->exc_type = p_type; + tstate->exc_value = p_value; + tstate->exc_traceback = p_traceback; + + Py_XDECREF(oldtype); + Py_XDECREF(oldvalue); + Py_XDECREF(oldtraceback); +} + /* Convenience functions to set a type error exception and return 0 */ int