[Python-checkins] cpython: Issue #14098: New functions PyErr_GetExcInfo and PyErr_SetExcInfo.

martin.v.loewis python-checkins at python.org
Thu Apr 19 14:34:23 CEST 2012


http://hg.python.org/cpython/rev/9fdec1354af4
changeset:   76418:9fdec1354af4
user:        Martin v. Löwis <martin at v.loewis.de>
date:        Thu Apr 19 14:33:43 2012 +0200
summary:
  Issue #14098: New functions PyErr_GetExcInfo and PyErr_SetExcInfo.
Patch by Stefan Behnel.

files:
  Doc/c-api/exceptions.rst  |  35 +++++++++++++++++++++++++++
  Include/pyerrors.h        |   2 +
  Lib/test/test_capi.py     |  23 +++++++++++++++++
  Misc/NEWS                 |   3 ++
  Modules/_testcapimodule.c |  24 ++++++++++++++++++
  Python/errors.c           |  33 +++++++++++++++++++++++++
  6 files changed, 120 insertions(+), 0 deletions(-)


diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -129,6 +129,41 @@
       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
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -82,6 +82,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 --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -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 --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
 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
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -1639,6 +1639,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;
 
@@ -2471,6 +2494,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 --git a/Python/errors.c b/Python/errors.c
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -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

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list