* 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
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
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
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
PyException_SetTraceback(val, tb);
}
+ .. deprecated:: 3.12
+
.. c:function:: PyObject* PyErr_GetHandledException(void)
: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
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,,
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,,
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,,
/* 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__.
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.)
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)
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,
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 *);
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) \
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()
_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'))
@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:
"""
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)
"PyErr_FormatV",
"PyErr_GetExcInfo",
"PyErr_GetHandledException",
+ "PyErr_GetRaisedException",
"PyErr_GivenExceptionMatches",
"PyErr_NewException",
"PyErr_NewExceptionWithDoc",
"PyErr_SetInterruptEx",
"PyErr_SetNone",
"PyErr_SetObject",
+ "PyErr_SetRaisedException",
"PyErr_SetString",
"PyErr_SyntaxLocation",
"PyErr_SyntaxLocationEx",
"PyExc_Warning",
"PyExc_ZeroDivisionError",
"PyExceptionClass_Name",
+ "PyException_GetArgs",
"PyException_GetCause",
"PyException_GetContext",
"PyException_GetTraceback",
+ "PyException_SetArgs",
"PyException_SetCause",
"PyException_SetContext",
"PyException_SetTraceback",
--- /dev/null
+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.
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]
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) {
// 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;
}
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;
}
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[] = {
{"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 */
};
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;
}
#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);
#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"
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;
}
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)
{
#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 */
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)
{
/* 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,
};
PyObject *PyExc_MemoryError = (PyObject *) &_PyExc_MemoryError;
+static PyBaseExceptionObject last_resort_memory_error = {
+ _PyObject_IMMORTAL_INIT(&_PyExc_MemoryError)
+};
/*
* BufferError extends Exception
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
#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 {
}
_Py_FatalErrorFormat(__func__, err, type->tp_name);
}
- Py_XDECREF(old_exc_type);
+ Py_XDECREF(old_exc);
Py_DECREF(type);
#endif
}
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)
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)
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)
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. */
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;
}
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;
}
}
}
}
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'",
Py_DECREF(funcstr);
}
Py_XDECREF(exc);
- Py_XDECREF(val);
- Py_XDECREF(tb);
}
else {
- _PyErr_Restore(tstate, exc, val, tb);
+ _PyErr_SetRaisedException(tstate, exc);
}
}
}
_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
_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)
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)
{
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.
}
+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);
+ }
}
}
}
+/* 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
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)
{
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);
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);
}
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");
PySys_WriteStderr(" ]\n");
}
- _PyErr_Restore(tstate, exc_type, exc_value, exc_tb);
+ _PyErr_SetRaisedException(tstate, exc);
}
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);
}
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;
}
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;
}
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) {
}
Py_DECREF(message);
}
- _PyErr_Restore(tstate, error_type, error_value, error_traceback);
+ _PyErr_SetRaisedException(tstate, error);
}
void
_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;
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)
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 *