mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
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:
parent
027adf42cd
commit
feec49c407
29 changed files with 477 additions and 172 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue