Issue #14098: New functions PyErr_GetExcInfo and PyErr_SetExcInfo.

Patch by Stefan Behnel.
This commit is contained in:
Martin v. Löwis 2012-04-19 14:33:43 +02:00
parent e27b3608ef
commit aa2efcb0bc
6 changed files with 120 additions and 0 deletions

View File

@ -129,6 +129,41 @@ in various ways. There is a separate error indicator for each thread.
exception state. 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.
.. versionadded:: 3.3
.. 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.
.. versionadded:: 3.3
.. c:function:: void PyErr_SetString(PyObject *type, const char *message) .. c:function:: void PyErr_SetString(PyObject *type, const char *message)
This is the most common way to set the error indicator. The first argument This is the most common way to set the error indicator. The first argument

View File

@ -82,6 +82,8 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void);
PyAPI_FUNC(void) PyErr_Clear(void); PyAPI_FUNC(void) PyErr_Clear(void);
PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **);
PyAPI_FUNC(void) PyErr_Restore(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__) || \ #if defined(__clang__) || \
(defined(__GNUC__) && \ (defined(__GNUC__) && \

View File

@ -55,6 +55,29 @@ class CAPITest(unittest.TestCase):
def test_memoryview_from_NULL_pointer(self): def test_memoryview_from_NULL_pointer(self):
self.assertRaises(ValueError, _testcapi.make_memoryview_from_NULL_pointer) 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.') @unittest.skipUnless(threading, 'Threading required for this test.')
class TestPendingCalls(unittest.TestCase): class TestPendingCalls(unittest.TestCase):

View File

@ -10,6 +10,9 @@ What's New in Python 3.3.0 Alpha 3?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #14098: New functions PyErr_GetExcInfo and PyErr_SetExcInfo.
Patch by Stefan Behnel.
- Issue #14385: It is now possible to use a custom type for the __builtins__ - Issue #14385: It is now possible to use a custom type for the __builtins__
namespace, instead of a dict. It can be used for sandboxing for example. namespace, instead of a dict. It can be used for sandboxing for example.
Raise also a NameError instead of ImportError if __build_class__ name if not Raise also a NameError instead of ImportError if __build_class__ name if not

View File

@ -1639,6 +1639,29 @@ raise_exception(PyObject *self, PyObject *args)
return NULL; 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; static int test_run_counter = 0;
@ -2471,6 +2494,7 @@ static PyMethodDef TestMethods[] = {
#endif #endif
{"traceback_print", traceback_print, METH_VARARGS}, {"traceback_print", traceback_print, METH_VARARGS},
{"exception_print", exception_print, METH_VARARGS}, {"exception_print", exception_print, METH_VARARGS},
{"set_exc_info", test_set_exc_info, METH_VARARGS},
{"argparsing", argparsing, METH_VARARGS}, {"argparsing", argparsing, METH_VARARGS},
{"code_newempty", code_newempty, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS},
{"make_exception_with_doc", (PyCFunction)make_exception_with_doc, {"make_exception_with_doc", (PyCFunction)make_exception_with_doc,

View File

@ -320,6 +320,39 @@ PyErr_Clear(void)
PyErr_Restore(NULL, NULL, NULL); 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 */ /* Convenience functions to set a type error exception and return 0 */
int int