GH-101578: Normalize the current exception (GH-101607)

* 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
This commit is contained in:
Mark Shannon 2023-02-08 09:31:12 +00:00 committed by GitHub
parent 027adf42cd
commit feec49c407
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 477 additions and 172 deletions

View 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

View 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,,

View 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__.

View 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.)

View 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,

View 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) \

View 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()

View 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)

View 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",

View File

@ -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.

View File

@ -2333,6 +2333,15 @@
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]

View 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;
}

View 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 */
};

View 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;
}

View 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);

View 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

View 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
}

4
PC/python3dll.c generated
View File

@ -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)

View 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;
}

View 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;
}
}

View 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);
}
}
}

View File

@ -27,54 +27,12 @@ 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;
if (traceback != NULL && !PyTraceBack_Check(traceback)) {
/* XXX Should never happen -- fatal error instead? */
/* Well, it could be None. */
Py_SETREF(traceback, NULL);
}
/* Save these in locals to safeguard against recursive
invocation through Py_XDECREF */
oldtype = tstate->curexc_type;
oldvalue = tstate->curexc_value;
oldtraceback = tstate->curexc_traceback;
tstate->curexc_type = type;
tstate->curexc_value = value;
tstate->curexc_traceback = traceback;
Py_XDECREF(oldtype);
Py_XDECREF(oldvalue);
Py_XDECREF(oldtraceback);
}
void
PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
{
PyThreadState *tstate = _PyThreadState_GET();
_PyErr_Restore(tstate, type, value, traceback);
}
_PyErr_StackItem *
_PyErr_GetTopmostException(PyThreadState *tstate)
{
_PyErr_StackItem *exc_info = tstate->exc_info;
assert(exc_info);
while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) &&
exc_info->previous_item != NULL)
{
exc_info = exc_info->previous_item;
}
return exc_info;
PyObject *old_exc = tstate->current_exception;
tstate->current_exception = exc;
Py_XDECREF(old_exc);
}
static PyObject*
@ -103,6 +61,80 @@ _PyErr_CreateException(PyObject *exception_type, PyObject *value)
return exc;
}
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
PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
{
PyThreadState *tstate = _PyThreadState_GET();
_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)
{
_PyErr_StackItem *exc_info = tstate->exc_info;
assert(exc_info);
while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) &&
exc_info->previous_item != NULL)
{
exc_info = exc_info->previous_item;
}
return exc_info;
}
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);
/* Normalize the exception */
if (value == NULL || (PyObject *)Py_TYPE(value) != exception) {
/* We must normalize the value right now */
PyObject *fixed_value;
/* Issue #23571: functions must not be called with an
exception set */
_PyErr_Clear(tstate);
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);
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);
fixed_value = _PyErr_CreateException(exception, value);
Py_XDECREF(value);
if (fixed_value == NULL) {
Py_DECREF(exc_value);
return;
}
value = fixed_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)
{

View File

@ -1036,9 +1036,7 @@
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);

View 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);
}

View 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);
}

View 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);

View 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;
}

View 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

View 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 *