]> granicus.if.org Git - python/commitdiff
Issue #14098: New functions PyErr_GetExcInfo and PyErr_SetExcInfo.
authorMartin v. Löwis <martin@v.loewis.de>
Thu, 19 Apr 2012 12:33:43 +0000 (14:33 +0200)
committerMartin v. Löwis <martin@v.loewis.de>
Thu, 19 Apr 2012 12:33:43 +0000 (14:33 +0200)
Patch by Stefan Behnel.

Doc/c-api/exceptions.rst
Include/pyerrors.h
Lib/test/test_capi.py
Misc/NEWS
Modules/_testcapimodule.c
Python/errors.c

index 877249bc6357e44be9658e503019434539537544..441dadea9cf89fd678591c65d72bc33940d7c208 100644 (file)
@@ -129,6 +129,41 @@ in various ways.  There is a separate error indicator for each thread.
       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)
 
    This is the most common way to set the error indicator.  The first argument
index 458420aa323ee067e3350f2463202aa02f6a73a2..1b80ecf26e4ef9fba7a1e942c42f5c0fd1233916 100644 (file)
@@ -82,6 +82,8 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void);
 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__) && \
index 2f94f90b5c3fafaf12041383191844828179b66a..07ec623210e8f18c4c2a81aa0a9047bdbef2d6de 100644 (file)
@@ -55,6 +55,29 @@ class CAPITest(unittest.TestCase):
     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):
 
index 879dc03cce2b0c8999992641a4da2107e5656ea5..574b52be46f5dcf70689e4413ae132c3fa79f755 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3.0 Alpha 3?
 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__
   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
index 093f205dfdf06d373e52e3b0842e182a9a7401dd..ec52a096042b3a08e94c9fd9507330bb1105f4b6 100644 (file)
@@ -1639,6 +1639,29 @@ raise_exception(PyObject *self, PyObject *args)
     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;
 
@@ -2471,6 +2494,7 @@ static PyMethodDef TestMethods[] = {
 #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,
index 36ab3d87fe4d156e031d36b4633f0c713db32dab..626b16e46fe7ab3bb0cb062b3796c0c4c71685a2 100644 (file)
@@ -320,6 +320,39 @@ PyErr_Clear(void)
     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