]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-101578: Normalize the current exception (GH-101607)
authorMark Shannon <mark@hotpy.org>
Wed, 8 Feb 2023 09:31:12 +0000 (09:31 +0000)
committerGitHub <noreply@github.com>
Wed, 8 Feb 2023 09:31:12 +0000 (09:31 +0000)
* Make sure that the current exception is always normalized.

* Remove redundant type and traceback fields for the current exception.

* Add new API functions: PyErr_GetRaisedException, PyErr_SetRaisedException

* Add new API functions: PyException_GetArgs, PyException_SetArgs

29 files changed:
Doc/c-api/exceptions.rst
Doc/data/stable_abi.dat
Include/cpython/pyerrors.h
Include/cpython/pystate.h
Include/internal/pycore_pyerrors.h
Include/pyerrors.h
Lib/test/test_capi/test_misc.py
Lib/test/test_exceptions.py
Lib/test/test_stable_abi_ctypes.py
Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst [new file with mode: 0644]
Misc/stable_abi.toml
Modules/_testcapi/heaptype.c
Modules/_testcapimodule.c
Modules/gcmodule.c
Objects/dictobject.c
Objects/exceptions.c
Objects/object.c
PC/python3dll.c
Parser/pegen.c
Python/bytecodes.c
Python/ceval.c
Python/errors.c
Python/generated_cases.c.h
Python/import.c
Python/initconfig.c
Python/pystate.c
Python/pythonrun.c
Python/sysmodule.c
Python/traceback.c

index 087e0a61d12d59a22880001a29e22cd0c389d438..de9b15edd6859a31f4a627cf2911a5a31f49d6b6 100644 (file)
@@ -400,8 +400,61 @@ Querying the error indicator
    recursively in subtuples) are searched for a match.
 
 
+.. c:function:: PyObject *PyErr_GetRaisedException(void)
+
+   Returns the exception currently being raised, clearing the exception at
+   the same time. Do not confuse this with the exception currently being
+   handled which can be accessed with  :c:func:`PyErr_GetHandledException`.
+
+   .. note::
+
+      This function is normally only used by code that needs to catch exceptions or
+      by code that needs to save and restore the error indicator temporarily, e.g.::
+
+         {
+            PyObject *exc = PyErr_GetRaisedException();
+
+            /* ... code that might produce other errors ... */
+
+            PyErr_SetRaisedException(exc);
+         }
+
+   .. versionadded:: 3.12
+
+
+.. c:function:: void PyErr_SetRaisedException(PyObject *exc)
+
+   Sets the exception currently being raised ``exc``.
+   If the exception is already set, it is cleared first.
+
+   ``exc`` must be a valid exception.
+   (Violating this rules will cause subtle problems later.)
+   This call consumes a reference to the ``exc`` object: you must own a
+   reference to that object before the call and after the call you no longer own
+   that reference.
+   (If you don't understand this, don't use this function. I warned you.)
+
+   .. note::
+
+      This function is normally only used by code that needs to save and restore the
+      error indicator temporarily.  Use :c:func:`PyErr_GetRaisedException` to save
+      the current exception, e.g.::
+
+         {
+            PyObject *exc = PyErr_GetRaisedException();
+
+            /* ... code that might produce other errors ... */
+
+            PyErr_SetRaisedException(exc);
+         }
+
+   .. versionadded:: 3.12
+
+
 .. c:function:: void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback)
 
+    As of 3.12, this function is deprecated. Use :c:func:`PyErr_GetRaisedException` instead.
+
    Retrieve the error indicator into three variables whose addresses are passed.
    If the error indicator is not set, set all three variables to ``NULL``.  If it is
    set, it will be cleared and you own a reference to each object retrieved.  The
@@ -421,10 +474,14 @@ Querying the error indicator
             PyErr_Restore(type, value, traceback);
          }
 
+   .. deprecated:: 3.12
+
 
 .. c:function:: void PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
 
-   Set  the error indicator from the three objects.  If the error indicator is
+    As of 3.12, this function is deprecated. Use :c:func:`PyErr_SetRaisedException` instead.
+
+   Set the error indicator from the three objects.  If the error indicator is
    already set, it is cleared first.  If the objects are ``NULL``, the error
    indicator is cleared.  Do not pass a ``NULL`` type and non-``NULL`` value or
    traceback.  The exception type should be a class.  Do not pass an invalid
@@ -440,9 +497,15 @@ Querying the error indicator
       error indicator temporarily.  Use :c:func:`PyErr_Fetch` to save the current
       error indicator.
 
+   .. deprecated:: 3.12
+
 
 .. c:function:: void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
 
+   As of 3.12, this function is deprecated.
+   Use :c:func:`PyErr_GetRaisedException` instead of :c:func:`PyErr_Fetch` to avoid
+   any possible de-normalization.
+
    Under certain circumstances, the values returned by :c:func:`PyErr_Fetch` below
    can be "unnormalized", meaning that ``*exc`` is a class object but ``*val`` is
    not an instance of the  same class.  This function can be used to instantiate
@@ -459,6 +522,8 @@ Querying the error indicator
            PyException_SetTraceback(val, tb);
          }
 
+   .. deprecated:: 3.12
+
 
 .. c:function:: PyObject* PyErr_GetHandledException(void)
 
@@ -704,6 +769,18 @@ Exception Objects
    :attr:`__suppress_context__` is implicitly set to ``True`` by this function.
 
 
+.. c:function:: PyObject* PyException_GetArgs(PyObject *ex)
+
+   Return args of the given exception as a new reference,
+   as accessible from Python through :attr:`args`.
+
+
+.. c:function:: void PyException_SetArgs(PyObject *ex, PyObject *args)
+
+   Set the args of the given exception,
+   as accessible from Python through :attr:`args`.
+
+
 .. _unicodeexceptions:
 
 Unicode Exception Objects
index 53895bbced8408de46935332af873f14c5a9676d..68ab0b5061f43481a027f8dadce40ab3ca88e52c 100644 (file)
@@ -139,6 +139,7 @@ function,PyErr_Format,3.2,,
 function,PyErr_FormatV,3.5,,
 function,PyErr_GetExcInfo,3.7,,
 function,PyErr_GetHandledException,3.11,,
+function,PyErr_GetRaisedException,3.12,,
 function,PyErr_GivenExceptionMatches,3.2,,
 function,PyErr_NewException,3.2,,
 function,PyErr_NewExceptionWithDoc,3.2,,
@@ -168,6 +169,7 @@ function,PyErr_SetInterrupt,3.2,,
 function,PyErr_SetInterruptEx,3.10,,
 function,PyErr_SetNone,3.2,,
 function,PyErr_SetObject,3.2,,
+function,PyErr_SetRaisedException,3.12,,
 function,PyErr_SetString,3.2,,
 function,PyErr_SyntaxLocation,3.2,,
 function,PyErr_SyntaxLocationEx,3.7,,
@@ -266,9 +268,11 @@ var,PyExc_Warning,3.2,,
 var,PyExc_WindowsError,3.7,on Windows,
 var,PyExc_ZeroDivisionError,3.2,,
 function,PyExceptionClass_Name,3.8,,
+function,PyException_GetArgs,3.12,,
 function,PyException_GetCause,3.2,,
 function,PyException_GetContext,3.2,,
 function,PyException_GetTraceback,3.2,,
+function,PyException_SetArgs,3.12,,
 function,PyException_SetCause,3.2,,
 function,PyException_SetContext,3.2,,
 function,PyException_SetTraceback,3.2,,
index 141341667795e8fadcd1bdc7b8650393d8fae53c..0d9cc9922f73687b1a9d99d431c2cd88d651ec75 100644 (file)
@@ -99,6 +99,7 @@ PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState *, PyObject **, PyObject **, Py
 /* Context manipulation (PEP 3134) */
 
 PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject *, PyObject *, PyObject *);
+PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *);
 
 /* Like PyErr_Format(), but saves current exception as __context__ and
    __cause__.
index f5db52f76e8f4fe48394630360a1355f97e9d726..be1fcb61fa2244d814ee3360b0c8003b9d0f813b 100644 (file)
@@ -166,9 +166,7 @@ struct _ts {
     PyObject *c_traceobj;
 
     /* The exception currently being raised */
-    PyObject *curexc_type;
-    PyObject *curexc_value;
-    PyObject *curexc_traceback;
+    PyObject *current_exception;
 
     /* Pointer to the top of the exception stack for the exceptions
      * we may be currently handling.  (See _PyErr_StackItem above.)
index 66f37942ef916afed58499e7593fb0338e281fa2..1bb4a9aa103898f24e519911d9cc7ba8323ca1e1 100644 (file)
@@ -20,7 +20,10 @@ extern void _PyErr_FiniTypes(PyInterpreterState *);
 static inline PyObject* _PyErr_Occurred(PyThreadState *tstate)
 {
     assert(tstate != NULL);
-    return tstate->curexc_type;
+    if (tstate->current_exception == NULL) {
+        return NULL;
+    }
+    return (PyObject *)Py_TYPE(tstate->current_exception);
 }
 
 static inline void _PyErr_ClearExcState(_PyErr_StackItem *exc_state)
@@ -37,10 +40,16 @@ PyAPI_FUNC(void) _PyErr_Fetch(
     PyObject **value,
     PyObject **traceback);
 
+extern PyObject *
+_PyErr_GetRaisedException(PyThreadState *tstate);
+
 PyAPI_FUNC(int) _PyErr_ExceptionMatches(
     PyThreadState *tstate,
     PyObject *exc);
 
+void
+_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc);
+
 PyAPI_FUNC(void) _PyErr_Restore(
     PyThreadState *tstate,
     PyObject *type,
index d5ac6af5b32c6cd7cc0ab7d198f2d24e81c1ab12..d089fa71779330afd29ce0411b882b305c37b1fe 100644 (file)
@@ -18,6 +18,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(PyObject *) PyErr_GetRaisedException(void);
+PyAPI_FUNC(void) PyErr_SetRaisedException(PyObject *);
 #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000
 PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void);
 PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *);
@@ -51,6 +53,10 @@ PyAPI_FUNC(void) PyException_SetCause(PyObject *, PyObject *);
 PyAPI_FUNC(PyObject *) PyException_GetContext(PyObject *);
 PyAPI_FUNC(void) PyException_SetContext(PyObject *, PyObject *);
 
+
+PyAPI_FUNC(PyObject *) PyException_GetArgs(PyObject *);
+PyAPI_FUNC(void) PyException_SetArgs(PyObject *, PyObject *);
+
 /* */
 
 #define PyExceptionClass_Check(x)                                       \
index 03e22d7a2d382d1e4ea5d3ea890b6c7e4f11e3fb..7612cddb1f6576bc78cf18659d7e2e3d3deb879f 100644 (file)
@@ -1553,5 +1553,44 @@ class Test_Pep523API(unittest.TestCase):
         self.do_test(func2)
 
 
+class Test_ErrSetAndRestore(unittest.TestCase):
+
+    def test_err_set_raised(self):
+        with self.assertRaises(ValueError):
+            _testcapi.err_set_raised(ValueError())
+        v = ValueError()
+        try:
+            _testcapi.err_set_raised(v)
+        except ValueError as ex:
+            self.assertIs(v, ex)
+
+    def test_err_restore(self):
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError)
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, 1)
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, 1, None)
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, ValueError())
+        try:
+            _testcapi.err_restore(KeyError, "hi")
+        except KeyError as k:
+            self.assertEqual("hi", k.args[0])
+        try:
+            1/0
+        except Exception as e:
+            tb = e.__traceback__
+        with self.assertRaises(ValueError):
+            _testcapi.err_restore(ValueError, 1, tb)
+        with self.assertRaises(TypeError):
+            _testcapi.err_restore(ValueError, 1, 0)
+        try:
+            _testcapi.err_restore(ValueError, 1, tb)
+        except ValueError as v:
+            self.assertEqual(1, v.args[0])
+            self.assertIs(tb, v.__traceback__.tb_next)
+
+
 if __name__ == "__main__":
     unittest.main()
index f629321458d8aeb5fcc2f2557054dfa15fda0146..4ae71e431c56dc17e52fe221f62f0aec54539976 100644 (file)
@@ -347,6 +347,7 @@ class ExceptionTests(unittest.TestCase):
                 _testcapi.raise_exception(BadException, 0)
             except RuntimeError as err:
                 exc, err, tb = sys.exc_info()
+                tb = tb.tb_next
                 co = tb.tb_frame.f_code
                 self.assertEqual(co.co_name, "__init__")
                 self.assertTrue(co.co_filename.endswith('test_exceptions.py'))
@@ -1415,8 +1416,8 @@ class ExceptionTests(unittest.TestCase):
     @cpython_only
     def test_recursion_normalizing_infinite_exception(self):
         # Issue #30697. Test that a RecursionError is raised when
-        # PyErr_NormalizeException() maximum recursion depth has been
-        # exceeded.
+        # maximum recursion depth has been exceeded when creating
+        # an exception
         code = """if 1:
             import _testcapi
             try:
@@ -1426,8 +1427,7 @@ class ExceptionTests(unittest.TestCase):
         """
         rc, out, err = script_helper.assert_python_failure("-c", code)
         self.assertEqual(rc, 1)
-        self.assertIn(b'RecursionError: maximum recursion depth exceeded '
-                      b'while normalizing an exception', err)
+        self.assertIn(b'RecursionError: maximum recursion depth exceeded', err)
         self.assertIn(b'Done.', out)
 
 
index 67c653428a6dee8ccddaa273e34387fb6384a950..e77c1c8409880d008ae7a2a4c00205569f3db46d 100644 (file)
@@ -172,6 +172,7 @@ SYMBOL_NAMES = (
     "PyErr_FormatV",
     "PyErr_GetExcInfo",
     "PyErr_GetHandledException",
+    "PyErr_GetRaisedException",
     "PyErr_GivenExceptionMatches",
     "PyErr_NewException",
     "PyErr_NewExceptionWithDoc",
@@ -195,6 +196,7 @@ SYMBOL_NAMES = (
     "PyErr_SetInterruptEx",
     "PyErr_SetNone",
     "PyErr_SetObject",
+    "PyErr_SetRaisedException",
     "PyErr_SetString",
     "PyErr_SyntaxLocation",
     "PyErr_SyntaxLocationEx",
@@ -292,9 +294,11 @@ SYMBOL_NAMES = (
     "PyExc_Warning",
     "PyExc_ZeroDivisionError",
     "PyExceptionClass_Name",
+    "PyException_GetArgs",
     "PyException_GetCause",
     "PyException_GetContext",
     "PyException_GetTraceback",
+    "PyException_SetArgs",
     "PyException_SetCause",
     "PyException_SetContext",
     "PyException_SetTraceback",
diff --git a/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst b/Misc/NEWS.d/next/C API/2023-02-06-16-14-30.gh-issue-101578.PW5fA9.rst
new file mode 100644 (file)
index 0000000..fc694f6
--- /dev/null
@@ -0,0 +1,13 @@
+Add new C-API functions for saving and restoring the current exception:
+``PyErr_GetRaisedException`` and ``PyErr_SetRaisedException``.
+These functions take and return a single exception rather than
+the triple of  ``PyErr_Fetch`` and ``PyErr_Restore``.
+This is less error prone and a bit more efficient.
+
+The three arguments forms of saving and restoring the
+current exception: ``PyErr_Fetch`` and ``PyErr_Restore``
+are deprecated.
+
+Also add ``PyException_GetArgs`` and ``PyException_SetArgs``
+as convenience functions to help dealing with
+exceptions in the C API.
index c716f403d638acf065ef9b5d614af264dbb22b17..21ff9616133445c46771a68dab641194d1d1ba11 100644 (file)
     added = '3.12'
 [function.PyVectorcall_Call]
     added = '3.12'
+[function.PyErr_GetRaisedException]
+    added = '3.12'
+[function.PyErr_SetRaisedException]
+    added = '3.12'
+[function.PyException_GetArgs]
+    added = '3.12'
+[function.PyException_SetArgs]
+    added = '3.12'
+
 [typedef.vectorcallfunc]
     added = '3.12'
 [function.PyObject_Vectorcall]
index bf80fd64d80b35c26301dee0b8b1a94f37609f1c..39639f7ed048f263b44bd6dacb870f9052c00081 100644 (file)
@@ -116,10 +116,10 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(
     PyObject *bases = NULL;
     PyObject *new = NULL;
     PyObject *meta_error_string = NULL;
-    PyObject *exc_type = NULL;
-    PyObject *exc_value = NULL;
-    PyObject *exc_traceback = NULL;
+    PyObject *exc = NULL;
     PyObject *result = NULL;
+    PyObject *message = NULL;
+    PyObject *args = NULL;
 
     metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
     if (metaclass_a == NULL) {
@@ -156,13 +156,19 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(
 
     // Assert that the correct exception was raised
     if (PyErr_ExceptionMatches(PyExc_TypeError)) {
-        PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
-
+        exc = PyErr_GetRaisedException();
+        args = PyException_GetArgs(exc);
+        if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) {
+            PyErr_SetString(PyExc_AssertionError,
+                    "TypeError args are not a one-tuple");
+            goto finally;
+        }
+        message = Py_NewRef(PyTuple_GET_ITEM(args, 0));
         meta_error_string = PyUnicode_FromString("metaclass conflict:");
         if (meta_error_string == NULL) {
             goto finally;
         }
-        int res = PyUnicode_Contains(exc_value, meta_error_string);
+        int res = PyUnicode_Contains(message, meta_error_string);
         if (res < 0) {
             goto finally;
         }
@@ -179,11 +185,11 @@ finally:
     Py_XDECREF(bases);
     Py_XDECREF(new);
     Py_XDECREF(meta_error_string);
-    Py_XDECREF(exc_type);
-    Py_XDECREF(exc_value);
-    Py_XDECREF(exc_traceback);
+    Py_XDECREF(exc);
+    Py_XDECREF(message);
     Py_XDECREF(class_a);
     Py_XDECREF(class_b);
+    Py_XDECREF(args);
     return result;
 }
 
index 8b2ce1a2cfd4bd81b5950e00438c077878c84b8d..3c411fa0d76358bdb48003b83b4e152466c5124e 100644 (file)
@@ -3470,6 +3470,41 @@ function_set_kw_defaults(PyObject *self, PyObject *args)
     Py_RETURN_NONE;
 }
 
+static PyObject *
+err_set_raised(PyObject *self, PyObject *exc)
+{
+    Py_INCREF(exc);
+    PyErr_SetRaisedException(exc);
+    assert(PyErr_Occurred());
+    return NULL;
+}
+
+static PyObject *
+err_restore(PyObject *self, PyObject *args) {
+    PyObject *type = NULL, *value = NULL, *traceback = NULL;
+    switch(PyTuple_Size(args)) {
+        case 3:
+            traceback = PyTuple_GetItem(args, 2);
+            Py_INCREF(traceback);
+            /* fall through */
+        case 2:
+            value = PyTuple_GetItem(args, 1);
+            Py_INCREF(value);
+            /* fall through */
+        case 1:
+            type = PyTuple_GetItem(args, 0);
+            Py_INCREF(type);
+            break;
+        default:
+            PyErr_SetString(PyExc_TypeError,
+                        "wrong number of arguments");
+            return NULL;
+    }
+    PyErr_Restore(type, value, traceback);
+    assert(PyErr_Occurred());
+    return NULL;
+}
+
 static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
 
 static PyMethodDef TestMethods[] = {
@@ -3622,6 +3657,8 @@ static PyMethodDef TestMethods[] = {
     {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
     {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
     {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
+    {"err_set_raised", err_set_raised, METH_O, NULL},
+    {"err_restore", err_restore, METH_VARARGS, NULL},
     {NULL, NULL} /* sentinel */
 };
 
index 6630faa6f4471d74b08e097acccf8bf2d5505c16..5879c5e11fe14a073c952a9226595ec3161aa34e 100644 (file)
@@ -2082,11 +2082,10 @@ PyGC_Collect(void)
         n = 0;
     }
     else {
-        PyObject *exc, *value, *tb;
         gcstate->collecting = 1;
-        _PyErr_Fetch(tstate, &exc, &value, &tb);
+        PyObject *exc = _PyErr_GetRaisedException(tstate);
         n = gc_collect_with_callback(tstate, NUM_GENERATIONS - 1);
-        _PyErr_Restore(tstate, exc, value, tb);
+        _PyErr_SetRaisedException(tstate, exc);
         gcstate->collecting = 0;
     }
 
index b9067213820b52e4e46d2628ba6009ed295085d7..fc658ca2f4b7f864d051fd2b75fd7e9531b4de4c 100644 (file)
@@ -1663,15 +1663,15 @@ PyDict_GetItem(PyObject *op, PyObject *key)
 #endif
 
     /* Preserve the existing exception */
-    PyObject *exc_type, *exc_value, *exc_tb;
     PyObject *value;
     Py_ssize_t ix; (void)ix;
 
-    _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
+
+    PyObject *exc = _PyErr_GetRaisedException(tstate);
     ix = _Py_dict_lookup(mp, key, hash, &value);
 
     /* Ignore any exception raised by the lookup */
-    _PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
+    _PyErr_SetRaisedException(tstate, exc);
 
 
     assert(ix >= 0 || value == NULL);
index db6f7d52804d6aa6d69f09cba850555420dfdf86..976f84dbf63c933f8093ed5e5def452bc6eb48ba 100644 (file)
@@ -8,6 +8,7 @@
 #include <Python.h>
 #include <stdbool.h>
 #include "pycore_ceval.h"         // _Py_EnterRecursiveCall
+#include "pycore_pyerrors.h"      // struct _PyErr_SetRaisedException
 #include "pycore_exceptions.h"    // struct _Py_exc_state
 #include "pycore_initconfig.h"
 #include "pycore_object.h"
@@ -288,13 +289,17 @@ BaseException_set_tb(PyBaseExceptionObject *self, PyObject *tb, void *Py_UNUSED(
         PyErr_SetString(PyExc_TypeError, "__traceback__ may not be deleted");
         return -1;
     }
-    else if (!(tb == Py_None || PyTraceBack_Check(tb))) {
+    if (PyTraceBack_Check(tb)) {
+        Py_XSETREF(self->traceback, Py_NewRef(tb));
+    }
+    else if (tb == Py_None) {
+        Py_CLEAR(self->traceback);
+    }
+    else {
         PyErr_SetString(PyExc_TypeError,
                         "__traceback__ must be a traceback or None");
         return -1;
     }
-
-    Py_XSETREF(self->traceback, Py_NewRef(tb));
     return 0;
 }
 
@@ -413,6 +418,20 @@ PyException_SetContext(PyObject *self, PyObject *context)
     Py_XSETREF(_PyBaseExceptionObject_cast(self)->context, context);
 }
 
+PyObject *
+PyException_GetArgs(PyObject *self)
+{
+    PyObject *args = _PyBaseExceptionObject_cast(self)->args;
+    return Py_NewRef(args);
+}
+
+void
+PyException_SetArgs(PyObject *self, PyObject *args)
+{
+    Py_INCREF(args);
+    Py_XSETREF(_PyBaseExceptionObject_cast(self)->args, args);
+}
+
 const char *
 PyExceptionClass_Name(PyObject *ob)
 {
@@ -3188,20 +3207,19 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
 
 #define MEMERRORS_SAVE 16
 
+static PyBaseExceptionObject last_resort_memory_error;
+
 static PyObject *
-MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds)
 {
     PyBaseExceptionObject *self;
-
-    /* If this is a subclass of MemoryError, don't use the freelist
-     * and just return a fresh object */
-    if (type != (PyTypeObject *) PyExc_MemoryError) {
-        return BaseException_new(type, args, kwds);
-    }
-
     struct _Py_exc_state *state = get_exc_state();
     if (state->memerrors_freelist == NULL) {
-        return BaseException_new(type, args, kwds);
+        if (!allow_allocation) {
+            return Py_NewRef(&last_resort_memory_error);
+        }
+        PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds);
+        return result;
     }
 
     /* Fetch object from freelist and revive it */
@@ -3221,6 +3239,35 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
     return (PyObject *)self;
 }
 
+static PyBaseExceptionObject last_resort_memory_error;
+
+static PyObject *
+MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    /* If this is a subclass of MemoryError, don't use the freelist
+     * and just return a fresh object */
+    if (type != (PyTypeObject *) PyExc_MemoryError) {
+        return BaseException_new(type, args, kwds);
+    }
+    return get_memory_error(1, args, kwds);
+}
+
+PyObject *
+_PyErr_NoMemory(PyThreadState *tstate)
+{
+    if (Py_IS_TYPE(PyExc_MemoryError, NULL)) {
+        /* PyErr_NoMemory() has been called before PyExc_MemoryError has been
+           initialized by _PyExc_Init() */
+        Py_FatalError("Out of memory and PyExc_MemoryError is not "
+                      "initialized yet");
+    }
+    PyObject *err = get_memory_error(0, NULL, NULL);
+    if (err != NULL) {
+        _PyErr_SetRaisedException(tstate, err);
+    }
+    return NULL;
+}
+
 static void
 MemoryError_dealloc(PyBaseExceptionObject *self)
 {
@@ -3252,6 +3299,7 @@ preallocate_memerrors(void)
     /* We create enough MemoryErrors and then decref them, which will fill
        up the freelist. */
     int i;
+
     PyObject *errors[MEMERRORS_SAVE];
     for (i = 0; i < MEMERRORS_SAVE; i++) {
         errors[i] = MemoryError_new((PyTypeObject *) PyExc_MemoryError,
@@ -3291,6 +3339,9 @@ static PyTypeObject _PyExc_MemoryError = {
 };
 PyObject *PyExc_MemoryError = (PyObject *) &_PyExc_MemoryError;
 
+static PyBaseExceptionObject last_resort_memory_error = {
+    _PyObject_IMMORTAL_INIT(&_PyExc_MemoryError)
+};
 
 /*
  *    BufferError extends Exception
index e940222c657e3c097778d16d16f268c7b8b2160f..7817c04ef5f5be4c0b01e35a7fce00827f45aaf3 100644 (file)
@@ -2416,10 +2416,10 @@ _Py_Dealloc(PyObject *op)
     destructor dealloc = type->tp_dealloc;
 #ifdef Py_DEBUG
     PyThreadState *tstate = _PyThreadState_GET();
-    PyObject *old_exc_type = tstate != NULL ? tstate->curexc_type : NULL;
+    PyObject *old_exc = tstate != NULL ? tstate->current_exception : NULL;
     // Keep the old exception type alive to prevent undefined behavior
     // on (tstate->curexc_type != old_exc_type) below
-    Py_XINCREF(old_exc_type);
+    Py_XINCREF(old_exc);
     // Make sure that type->tp_name remains valid
     Py_INCREF(type);
 #endif
@@ -2432,12 +2432,12 @@ _Py_Dealloc(PyObject *op)
 #ifdef Py_DEBUG
     // gh-89373: The tp_dealloc function must leave the current exception
     // unchanged.
-    if (tstate != NULL && tstate->curexc_type != old_exc_type) {
+    if (tstate != NULL && tstate->current_exception != old_exc) {
         const char *err;
-        if (old_exc_type == NULL) {
+        if (old_exc == NULL) {
             err = "Deallocator of type '%s' raised an exception";
         }
-        else if (tstate->curexc_type == NULL) {
+        else if (tstate->current_exception == NULL) {
             err = "Deallocator of type '%s' cleared the current exception";
         }
         else {
@@ -2448,7 +2448,7 @@ _Py_Dealloc(PyObject *op)
         }
         _Py_FatalErrorFormat(__func__, err, type->tp_name);
     }
-    Py_XDECREF(old_exc_type);
+    Py_XDECREF(old_exc);
     Py_DECREF(type);
 #endif
 }
index 931f316bb998439f73375d517a590808df49a9b2..e300819365756ef4e1a9c92915cf4a2c83ef494a 100755 (executable)
@@ -198,6 +198,7 @@ EXPORT_FUNC(PyErr_Format)
 EXPORT_FUNC(PyErr_FormatV)
 EXPORT_FUNC(PyErr_GetExcInfo)
 EXPORT_FUNC(PyErr_GetHandledException)
+EXPORT_FUNC(PyErr_GetRaisedException)
 EXPORT_FUNC(PyErr_GivenExceptionMatches)
 EXPORT_FUNC(PyErr_NewException)
 EXPORT_FUNC(PyErr_NewExceptionWithDoc)
@@ -227,6 +228,7 @@ EXPORT_FUNC(PyErr_SetInterrupt)
 EXPORT_FUNC(PyErr_SetInterruptEx)
 EXPORT_FUNC(PyErr_SetNone)
 EXPORT_FUNC(PyErr_SetObject)
+EXPORT_FUNC(PyErr_SetRaisedException)
 EXPORT_FUNC(PyErr_SetString)
 EXPORT_FUNC(PyErr_SyntaxLocation)
 EXPORT_FUNC(PyErr_SyntaxLocationEx)
@@ -255,9 +257,11 @@ EXPORT_FUNC(PyEval_ReleaseThread)
 EXPORT_FUNC(PyEval_RestoreThread)
 EXPORT_FUNC(PyEval_SaveThread)
 EXPORT_FUNC(PyEval_ThreadsInitialized)
+EXPORT_FUNC(PyException_GetArgs)
 EXPORT_FUNC(PyException_GetCause)
 EXPORT_FUNC(PyException_GetContext)
 EXPORT_FUNC(PyException_GetTraceback)
+EXPORT_FUNC(PyException_SetArgs)
 EXPORT_FUNC(PyException_SetCause)
 EXPORT_FUNC(PyException_SetContext)
 EXPORT_FUNC(PyException_SetTraceback)
index d84e06861edefce81a32360924958e434e1a8e66..94dd9de8a431c115705f619199fe7b10ffe6fc55 100644 (file)
@@ -643,13 +643,10 @@ _PyPegen_number_token(Parser *p)
         PyThreadState *tstate = _PyThreadState_GET();
         // The only way a ValueError should happen in _this_ code is via
         // PyLong_FromString hitting a length limit.
-        if (tstate->curexc_type == PyExc_ValueError &&
-            tstate->curexc_value != NULL) {
-            PyObject *type, *value, *tb;
-            // This acts as PyErr_Clear() as we're replacing curexc.
-            PyErr_Fetch(&type, &value, &tb);
-            Py_XDECREF(tb);
-            Py_DECREF(type);
+        if (tstate->current_exception != NULL &&
+            Py_TYPE(tstate->current_exception) == (PyTypeObject *)PyExc_ValueError
+        ) {
+            PyObject *exc = PyErr_GetRaisedException();
             /* Intentionally omitting columns to avoid a wall of 1000s of '^'s
              * on the error message. Nobody is going to overlook their huge
              * numeric literal once given the line. */
@@ -659,8 +656,8 @@ _PyPegen_number_token(Parser *p)
                 t->end_lineno, -1 /* end_col_offset */,
                 "%S - Consider hexadecimal for huge integer literals "
                 "to avoid decimal conversion limits.",
-                value);
-            Py_DECREF(value);
+                exc);
+            Py_DECREF(exc);
         }
         return NULL;
     }
index 9633f34212a68d08adb8a27203579b6126d0352a..1169d8d172dd57144d4112c5afcfdee7b95706d5 100644 (file)
@@ -804,9 +804,7 @@ dummy_func(
                 DECREF_INPUTS();
             }
             else {
-                PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value));
-                PyObject *exc_traceback = PyException_GetTraceback(exc_value);
-                _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback);
+                _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value));
                 goto exception_unwind;
             }
         }
index ecb5bf9655553edd8cff268c7a51d1e8609fa1c5..a91f5baca8853eb1ff0e97b7056e48fa69335a24 100644 (file)
@@ -2902,13 +2902,13 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs)
         }
     }
     else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
-        PyObject *exc, *val, *tb;
-        _PyErr_Fetch(tstate, &exc, &val, &tb);
-        if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) {
+        PyObject *exc = _PyErr_GetRaisedException(tstate);
+        PyObject *args = ((PyBaseExceptionObject *)exc)->args;
+        if (exc && PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) {
             _PyErr_Clear(tstate);
             PyObject *funcstr = _PyObject_FunctionStr(func);
             if (funcstr != NULL) {
-                PyObject *key = PyTuple_GET_ITEM(val, 0);
+                PyObject *key = PyTuple_GET_ITEM(args, 0);
                 _PyErr_Format(
                     tstate, PyExc_TypeError,
                     "%U got multiple values for keyword argument '%S'",
@@ -2916,11 +2916,9 @@ format_kwargs_error(PyThreadState *tstate, PyObject *func, PyObject *kwargs)
                 Py_DECREF(funcstr);
             }
             Py_XDECREF(exc);
-            Py_XDECREF(val);
-            Py_XDECREF(tb);
         }
         else {
-            _PyErr_Restore(tstate, exc, val, tb);
+            _PyErr_SetRaisedException(tstate, exc);
         }
     }
 }
index 05ef62246ec0a46b14c7af49ef32a55ff6f52049..f573bed3d63ef047480004fd71c0562f43b245d9 100644 (file)
@@ -27,32 +27,84 @@ static PyObject *
 _PyErr_FormatV(PyThreadState *tstate, PyObject *exception,
                const char *format, va_list vargs);
 
-
 void
-_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value,
-               PyObject *traceback)
+_PyErr_SetRaisedException(PyThreadState *tstate, PyObject *exc)
 {
-    PyObject *oldtype, *oldvalue, *oldtraceback;
+    PyObject *old_exc = tstate->current_exception;
+    tstate->current_exception = exc;
+    Py_XDECREF(old_exc);
+}
 
-    if (traceback != NULL && !PyTraceBack_Check(traceback)) {
-        /* XXX Should never happen -- fatal error instead? */
-        /* Well, it could be None. */
-        Py_SETREF(traceback, NULL);
+static PyObject*
+_PyErr_CreateException(PyObject *exception_type, PyObject *value)
+{
+    PyObject *exc;
+
+    if (value == NULL || value == Py_None) {
+        exc = _PyObject_CallNoArgs(exception_type);
+    }
+    else if (PyTuple_Check(value)) {
+        exc = PyObject_Call(exception_type, value, NULL);
+    }
+    else {
+        exc = PyObject_CallOneArg(exception_type, value);
     }
 
-    /* Save these in locals to safeguard against recursive
-       invocation through Py_XDECREF */
-    oldtype = tstate->curexc_type;
-    oldvalue = tstate->curexc_value;
-    oldtraceback = tstate->curexc_traceback;
+    if (exc != NULL && !PyExceptionInstance_Check(exc)) {
+        PyErr_Format(PyExc_TypeError,
+                     "calling %R should have returned an instance of "
+                     "BaseException, not %s",
+                     exception_type, Py_TYPE(exc)->tp_name);
+        Py_CLEAR(exc);
+    }
 
-    tstate->curexc_type = type;
-    tstate->curexc_value = value;
-    tstate->curexc_traceback = traceback;
+    return exc;
+}
 
-    Py_XDECREF(oldtype);
-    Py_XDECREF(oldvalue);
-    Py_XDECREF(oldtraceback);
+void
+_PyErr_Restore(PyThreadState *tstate, PyObject *type, PyObject *value,
+               PyObject *traceback)
+{
+    if (type == NULL) {
+        assert(value == NULL);
+        assert(traceback == NULL);
+        _PyErr_SetRaisedException(tstate, NULL);
+        return;
+    }
+    assert(PyExceptionClass_Check(type));
+    if (value != NULL && type == (PyObject *)Py_TYPE(value)) {
+        /* Already normalized */
+        assert(((PyBaseExceptionObject *)value)->traceback != Py_None);
+    }
+    else {
+        PyObject *exc = _PyErr_CreateException(type, value);
+        Py_XDECREF(value);
+        if (exc == NULL) {
+            Py_DECREF(type);
+            Py_XDECREF(traceback);
+            return;
+        }
+        value = exc;
+    }
+    assert(PyExceptionInstance_Check(value));
+    if (traceback != NULL && !PyTraceBack_Check(traceback)) {
+        if (traceback == Py_None) {
+            Py_DECREF(Py_None);
+            traceback = NULL;
+        }
+        else {
+            PyErr_SetString(PyExc_TypeError, "traceback must be a Traceback or None");
+            Py_XDECREF(value);
+            Py_DECREF(type);
+            Py_XDECREF(traceback);
+            return;
+        }
+    }
+    PyObject *old_traceback = ((PyBaseExceptionObject *)value)->traceback;
+    ((PyBaseExceptionObject *)value)->traceback = traceback;
+    Py_XDECREF(old_traceback);
+    _PyErr_SetRaisedException(tstate, value);
+    Py_DECREF(type);
 }
 
 void
@@ -62,6 +114,12 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
     _PyErr_Restore(tstate, type, value, traceback);
 }
 
+void
+PyErr_SetRaisedException(PyObject *exc)
+{
+    PyThreadState *tstate = _PyThreadState_GET();
+    _PyErr_SetRaisedException(tstate, exc);
+}
 
 _PyErr_StackItem *
 _PyErr_GetTopmostException(PyThreadState *tstate)
@@ -77,32 +135,6 @@ _PyErr_GetTopmostException(PyThreadState *tstate)
     return exc_info;
 }
 
-static PyObject*
-_PyErr_CreateException(PyObject *exception_type, PyObject *value)
-{
-    PyObject *exc;
-
-    if (value == NULL || value == Py_None) {
-        exc = _PyObject_CallNoArgs(exception_type);
-    }
-    else if (PyTuple_Check(value)) {
-        exc = PyObject_Call(exception_type, value, NULL);
-    }
-    else {
-        exc = PyObject_CallOneArg(exception_type, value);
-    }
-
-    if (exc != NULL && !PyExceptionInstance_Check(exc)) {
-        PyErr_Format(PyExc_TypeError,
-                     "calling %R should have returned an instance of "
-                     "BaseException, not %s",
-                     exception_type, Py_TYPE(exc)->tp_name);
-        Py_CLEAR(exc);
-    }
-
-    return exc;
-}
-
 void
 _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
 {
@@ -117,30 +149,29 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
                       exception);
         return;
     }
-
     Py_XINCREF(value);
-    exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
-    if (exc_value != NULL && exc_value != Py_None) {
-        /* Implicit exception chaining */
-        Py_INCREF(exc_value);
-        if (value == NULL || !PyExceptionInstance_Check(value)) {
-            /* We must normalize the value right now */
-            PyObject *fixed_value;
-
-            /* Issue #23571: functions must not be called with an
-               exception set */
-            _PyErr_Clear(tstate);
+    /* Normalize the exception */
+    if (value == NULL || (PyObject *)Py_TYPE(value) != exception) {
+        /* We must normalize the value right now */
+        PyObject *fixed_value;
 
-            fixed_value = _PyErr_CreateException(exception, value);
-            Py_XDECREF(value);
-            if (fixed_value == NULL) {
-                Py_DECREF(exc_value);
-                return;
-            }
+        /* Issue #23571: functions must not be called with an
+            exception set */
+        _PyErr_Clear(tstate);
 
-            value = fixed_value;
+        fixed_value = _PyErr_CreateException(exception, value);
+        Py_XDECREF(value);
+        if (fixed_value == NULL) {
+            return;
         }
 
+        value = fixed_value;
+    }
+
+    exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
+    if (exc_value != NULL && exc_value != Py_None) {
+        /* Implicit exception chaining */
+        Py_INCREF(exc_value);
         /* Avoid creating new reference cycles through the
            context chain, while taking care not to hang on
            pre-existing ones.
@@ -414,17 +445,34 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
 }
 
 
+PyObject *
+_PyErr_GetRaisedException(PyThreadState *tstate) {
+    PyObject *exc = tstate->current_exception;
+    tstate->current_exception = NULL;
+    return exc;
+}
+
+PyObject *
+PyErr_GetRaisedException(void)
+{
+    PyThreadState *tstate = _PyThreadState_GET();
+    return _PyErr_GetRaisedException(tstate);
+}
+
 void
 _PyErr_Fetch(PyThreadState *tstate, PyObject **p_type, PyObject **p_value,
              PyObject **p_traceback)
 {
-    *p_type = tstate->curexc_type;
-    *p_value = tstate->curexc_value;
-    *p_traceback = tstate->curexc_traceback;
-
-    tstate->curexc_type = NULL;
-    tstate->curexc_value = NULL;
-    tstate->curexc_traceback = NULL;
+    PyObject *exc = _PyErr_GetRaisedException(tstate);
+    *p_value = exc;
+    if (exc == NULL) {
+        *p_type = NULL;
+        *p_traceback = NULL;
+    }
+    else {
+        *p_type = Py_NewRef(Py_TYPE(exc));
+        *p_traceback = Py_XNewRef(((PyBaseExceptionObject *)exc)->traceback);
+    }
 }
 
 
@@ -597,6 +645,28 @@ _PyErr_ChainExceptions(PyObject *typ, PyObject *val, PyObject *tb)
     }
 }
 
+/* Like PyErr_SetRaisedException(), but if an exception is already set,
+   set the context associated with it.
+
+   The caller is responsible for ensuring that this call won't create
+   any cycles in the exception context chain. */
+void
+_PyErr_ChainExceptions1(PyObject *exc)
+{
+    if (exc == NULL) {
+        return;
+    }
+    PyThreadState *tstate = _PyThreadState_GET();
+    if (_PyErr_Occurred(tstate)) {
+        PyObject *exc2 = _PyErr_GetRaisedException(tstate);
+        PyException_SetContext(exc2, exc);
+        _PyErr_SetRaisedException(tstate, exc2);
+    }
+    else {
+        _PyErr_SetRaisedException(tstate, exc);
+    }
+}
+
 /* Set the currently set exception's context to the given exception.
 
    If the provided exc_info is NULL, then the current Python thread state's
@@ -706,19 +776,6 @@ PyErr_BadArgument(void)
     return 0;
 }
 
-PyObject *
-_PyErr_NoMemory(PyThreadState *tstate)
-{
-    if (Py_IS_TYPE(PyExc_MemoryError, NULL)) {
-        /* PyErr_NoMemory() has been called before PyExc_MemoryError has been
-           initialized by _PyExc_Init() */
-        Py_FatalError("Out of memory and PyExc_MemoryError is not "
-                      "initialized yet");
-    }
-    _PyErr_SetNone(tstate, PyExc_MemoryError);
-    return NULL;
-}
-
 PyObject *
 PyErr_NoMemory(void)
 {
index f38286441be4b3ab185d8889df945d8bef93b8bd..09eb6893ebf6b4bcf404984a99695fb655341ada 100644 (file)
                 Py_DECREF(exc_value);
             }
             else {
-                PyObject *exc_type = Py_NewRef(Py_TYPE(exc_value));
-                PyObject *exc_traceback = PyException_GetTraceback(exc_value);
-                _PyErr_Restore(tstate, exc_type, Py_NewRef(exc_value), exc_traceback);
+                _PyErr_SetRaisedException(tstate, Py_NewRef(exc_value));
                 goto exception_unwind;
             }
             STACK_SHRINK(2);
index da6c15c5fd4144e23711d851cf0068d363c82159..1318c09d9b3212fd918c30472faedccce55efda7 100644 (file)
@@ -1592,6 +1592,13 @@ remove_importlib_frames(PyThreadState *tstate)
         Py_DECREF(code);
         tb = next;
     }
+    assert(PyExceptionInstance_Check(value));
+    assert((PyObject *)Py_TYPE(value) == exception);
+    if (base_tb == NULL) {
+        base_tb = Py_None;
+        Py_INCREF(Py_None);
+    }
+    PyException_SetTraceback(value, base_tb);
 done:
     _PyErr_Restore(tstate, exception, value, base_tb);
 }
index d7b2dc4a2974255ffc7131aaf13fd4abec885ff9..deec805a6b1ca4783eeb0ea79e8bbb6961eae34c 100644 (file)
@@ -3143,8 +3143,7 @@ init_dump_ascii_wstr(const wchar_t *str)
 void
 _Py_DumpPathConfig(PyThreadState *tstate)
 {
-    PyObject *exc_type, *exc_value, *exc_tb;
-    _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
+    PyObject *exc = _PyErr_GetRaisedException(tstate);
 
     PySys_WriteStderr("Python path configuration:\n");
 
@@ -3202,5 +3201,5 @@ _Py_DumpPathConfig(PyThreadState *tstate)
         PySys_WriteStderr("  ]\n");
     }
 
-    _PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
+    _PyErr_SetRaisedException(tstate, exc);
 }
index ed8c2e212a55391f366f0a52c9cf63b40c904f95..1261092d1435fa12799528681e0db9a9268a8d4e 100644 (file)
@@ -1375,9 +1375,7 @@ PyThreadState_Clear(PyThreadState *tstate)
     Py_CLEAR(tstate->dict);
     Py_CLEAR(tstate->async_exc);
 
-    Py_CLEAR(tstate->curexc_type);
-    Py_CLEAR(tstate->curexc_value);
-    Py_CLEAR(tstate->curexc_traceback);
+    Py_CLEAR(tstate->current_exception);
 
     Py_CLEAR(tstate->exc_state.exc_value);
 
index 35292b6478a833a24f00b03c87fc26c7a0aa077d..6a4d593768690af86a534f54f96d96cea891d56a 100644 (file)
@@ -748,13 +748,10 @@ _Py_HandleSystemExit(int *exitcode_p)
     }
 
  done:
-    /* Restore and clear the exception info, in order to properly decref
-     * the exception, value, and traceback.      If we just exit instead,
-     * these leak, which confuses PYTHONDUMPREFS output, and may prevent
-     * some finalizers from running.
-     */
-    PyErr_Restore(exception, value, tb);
-    PyErr_Clear();
+    /* Cleanup the exception */
+    Py_CLEAR(exception);
+    Py_CLEAR(value);
+    Py_CLEAR(tb);
     *exitcode_p = exitcode;
     return 1;
 }
index f9f766a94d1464f655e03733825438441e636536..6e81ef92b67f70ec5822f4868b541928eece9630 100644 (file)
@@ -66,12 +66,11 @@ _PySys_GetAttr(PyThreadState *tstate, PyObject *name)
     if (sd == NULL) {
         return NULL;
     }
-    PyObject *exc_type, *exc_value, *exc_tb;
-    _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb);
+    PyObject *exc = _PyErr_GetRaisedException(tstate);
     /* XXX Suppress a new exception if it was raised and restore
      * the old one. */
     PyObject *value = _PyDict_GetItemWithError(sd, name);
-    _PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
+    _PyErr_SetRaisedException(tstate, exc);
     return value;
 }
 
@@ -3704,11 +3703,10 @@ static void
 sys_format(PyObject *key, FILE *fp, const char *format, va_list va)
 {
     PyObject *file, *message;
-    PyObject *error_type, *error_value, *error_traceback;
     const char *utf8;
     PyThreadState *tstate = _PyThreadState_GET();
 
-    _PyErr_Fetch(tstate, &error_type, &error_value, &error_traceback);
+    PyObject *error = _PyErr_GetRaisedException(tstate);
     file = _PySys_GetAttr(tstate, key);
     message = PyUnicode_FromFormatV(format, va);
     if (message != NULL) {
@@ -3720,7 +3718,7 @@ sys_format(PyObject *key, FILE *fp, const char *format, va_list va)
         }
         Py_DECREF(message);
     }
-    _PyErr_Restore(tstate, error_type, error_value, error_traceback);
+    _PyErr_SetRaisedException(tstate, error);
 }
 
 void
index da26c9b260a3bd90a2a4501b5f411bfbbaf14685..31b85e77575efaeb59284015c6e4f59a8de2ac7e 100644 (file)
@@ -249,6 +249,8 @@ PyTraceBack_Here(PyFrameObject *frame)
         _PyErr_ChainExceptions(exc, val, tb);
         return -1;
     }
+    assert(PyExceptionInstance_Check(val));
+    PyException_SetTraceback(val, newtb);
     PyErr_Restore(exc, val, newtb);
     Py_XDECREF(tb);
     return 0;
@@ -260,13 +262,12 @@ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno)
     PyObject *globals;
     PyCodeObject *code;
     PyFrameObject *frame;
-    PyObject *exc, *val, *tb;
     PyThreadState *tstate = _PyThreadState_GET();
 
     /* Save and clear the current exception. Python functions must not be
        called with an exception set. Calling Python functions happens when
        the codec of the filesystem encoding is implemented in pure Python. */
-    _PyErr_Fetch(tstate, &exc, &val, &tb);
+    PyObject *exc = _PyErr_GetRaisedException(tstate);
 
     globals = PyDict_New();
     if (!globals)
@@ -283,13 +284,13 @@ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno)
         goto error;
     frame->f_lineno = lineno;
 
-    _PyErr_Restore(tstate, exc, val, tb);
+    _PyErr_SetRaisedException(tstate, exc);
     PyTraceBack_Here(frame);
     Py_DECREF(frame);
     return;
 
 error:
-    _PyErr_ChainExceptions(exc, val, tb);
+    _PyErr_ChainExceptions1(exc);
 }
 
 static PyObject *