8000 GH-101578: Normalize the current exception by markshannon · Pull Request #101607 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-101578: Normalize the current exception #101607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 8, 2023
Merged
10000
Next Next commit
Make sure that the current exception is always normalized.
  • Loading branch information
markshannon committed Feb 6, 2023
commit 87889bcbbd3eab8daaad6480f4943bedd8af8743
6 changes: 6 additions & 0 deletions Include/internal/pycore_pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ PyAPI_FUNC(void) _PyErr_Fetch(
PyObject **value,
PyObject **traceback);

extern PyObject *
_PyErr_Fetch1(PyThreadState *tstate);

PyAPI_FUNC(int) _PyErr_ExceptionMatches(
PyThreadState *tstate,
PyObject *exc);

void
_PyErr_Restore1(PyThreadState *tstate, PyObject *exc);

PyAPI_FUNC(void) _PyErr_Restore(
PyThreadState *tstate,
PyObject *type,
Expand Down
6 changes: 6 additions & 0 deletions Include/pyerrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_Fetch1(void);
PyAPI_FUNC(void) PyErr_Restore1(PyObject *);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030b0000
PyAPI_FUNC(PyObject*) PyErr_GetHandledException(void);
PyAPI_FUNC(void) PyErr_SetHandledException(PyObject *);
Expand Down Expand Up @@ -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) \
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ def test_capi2():
_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'))
Expand Down Expand Up @@ -1415,8 +1416,8 @@ def gen():
@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:
Expand All @@ -1426,8 +1427,7 @@ def test_recursion_normalizing_infinite_exception(self):
"""
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)


Expand Down
22 changes: 13 additions & 9 deletions Modules/_testcapi/heaptype.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,9 @@ 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;

metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
if (metaclass_a == NULL) {
Expand Down Expand Up @@ -156,13 +155,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_Fetch1();
PyObject *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;
}
PyObject *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;
}
Expand All @@ -179,9 +184,8 @@ test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(
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);
return result;
Expand Down
75 changes: 63 additions & 12 deletions Objects/exceptions.c
9E19
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <Python.h>
#include <stdbool.h>
#include "pycore_ceval.h" // _Py_EnterRecursiveCall
#include "pycore_pyerrors.h" // struct _PyErr_Restore1
#include "pycore_exceptions.h" // struct _Py_exc_state
#include "pycore_initconfig.h"
#include "pycore_object.h"
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 */
Expand All @@ -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_Restore1(tstate, err);
}
return NULL;
}

static void
MemoryError_dealloc(PyBaseExceptionObject *self)
{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
12 changes: 5 additions & 7 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2905,25 +2905,23 @@ 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_Fetch1(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'",
funcstr, key);
Py_DECREF(funcstr);
}
Py_XDECREF(exc);
Py_XDECREF(val);
Py_XDECREF(tb);
}
else {
_PyErr_Restore(tstate, exc, val, tb);
_PyErr_Restore1(tstate, exc);
}
}
}
Expand Down
Loading
0