diff --git a/Include/pyerrors.h b/Include/pyerrors.h --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -205,8 +205,6 @@ PyAPI_DATA(PyObject *) PyExc_WindowsError; #endif -PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst; - /* Predefined warning categories */ PyAPI_DATA(PyObject *) PyExc_Warning; PyAPI_DATA(PyObject *) PyExc_UserWarning; diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -10,6 +10,7 @@ from test.support import (TESTFN, captured_output, check_impl_detail, check_warnings, cpython_only, gc_collect, run_unittest, no_tracing, unlink) +from test.script_helper import assert_python_failure class NaiveException(Exception): def __init__(self, x): @@ -945,6 +946,37 @@ os.listdir(__file__) self.assertEqual(cm.exception.errno, errno.ENOTDIR, cm.exception) + @cpython_only + def test_recursion_normalizing_exception(self): + # The test causes the instantiation of MyException to fail during the + # exception normalization while the maximum recursion depth has been + # reached. See issue 22898. + code = """if 1: + import sys, traceback + + class MyException(Exception): + def __init__(self, *args): + 1/0 + + def gen(): + f = open(%a, mode='rb', buffering=0) + yield + + g = gen() + next(g) + recursionlimit = sys.getrecursionlimit() + sys.setrecursionlimit(len(traceback.extract_stack())) + try: + g.throw(MyException) + finally: + sys.setrecursionlimit(recursionlimit) + print('Done.') + """ % __file__ + rc, out, err = assert_python_failure("-c", code) + # Check that the program does not fail with SIGSEV or SIGABRT. + self.assertEqual(rc, 1) + self.assertIn(b'Done.', out) + class ImportErrorTests(unittest.TestCase): diff --git a/Objects/exceptions.c b/Objects/exceptions.c --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2369,12 +2369,6 @@ -/* Pre-computed RuntimeError instance for when recursion depth is reached. - Meant to be used when normalizing the exception for exceeding the recursion - depth will cause its own infinite recursion. -*/ -PyObject *PyExc_RecursionErrorInst = NULL; - #define PRE_INIT(TYPE) \ if (!(_PyExc_ ## TYPE.tp_flags & Py_TPFLAGS_READY)) { \ if (PyType_Ready(&_PyExc_ ## TYPE) < 0) \ @@ -2628,37 +2622,11 @@ ADD_ERRNO(TimeoutError, ETIMEDOUT); preallocate_memerrors(); - - if (!PyExc_RecursionErrorInst) { - PyExc_RecursionErrorInst = BaseException_new(&_PyExc_RuntimeError, NULL, NULL); - if (!PyExc_RecursionErrorInst) - Py_FatalError("Cannot pre-allocate RuntimeError instance for " - "recursion errors"); - else { - PyBaseExceptionObject *err_inst = - (PyBaseExceptionObject *)PyExc_RecursionErrorInst; - PyObject *args_tuple; - PyObject *exc_message; - exc_message = PyUnicode_FromString("maximum recursion depth exceeded"); - if (!exc_message) - Py_FatalError("cannot allocate argument for RuntimeError " - "pre-allocation"); - args_tuple = PyTuple_Pack(1, exc_message); - if (!args_tuple) - Py_FatalError("cannot allocate tuple for RuntimeError " - "pre-allocation"); - Py_DECREF(exc_message); - if (BaseException_init(err_inst, args_tuple, NULL)) - Py_FatalError("init of pre-allocated RuntimeError failed"); - Py_DECREF(args_tuple); - } - } } void _PyExc_Fini(void) { - Py_CLEAR(PyExc_RecursionErrorInst); free_preallocated_memerrors(); Py_CLEAR(errnomap); } diff --git a/PC/python3.def b/PC/python3.def --- a/PC/python3.def +++ b/PC/python3.def @@ -193,7 +193,6 @@ PyExc_OSError=python34.PyExc_OSError DATA PyExc_OverflowError=python34.PyExc_OverflowError DATA PyExc_PendingDeprecationWarning=python34.PyExc_PendingDeprecationWarning DATA - PyExc_RecursionErrorInst=python34.PyExc_RecursionErrorInst DATA PyExc_ReferenceError=python34.PyExc_ReferenceError DATA PyExc_RuntimeError=python34.PyExc_RuntimeError DATA PyExc_RuntimeWarning=python34.PyExc_RuntimeWarning DATA diff --git a/PC/python34stub.def b/PC/python34stub.def --- a/PC/python34stub.def +++ b/PC/python34stub.def @@ -192,7 +192,6 @@ PyExc_OSError PyExc_OverflowError PyExc_PendingDeprecationWarning -PyExc_RecursionErrorInst PyExc_ReferenceError PyExc_RuntimeError PyExc_RuntimeWarning diff --git a/Python/_warnings.c b/Python/_warnings.c --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -518,10 +518,19 @@ *lineno = PyFrame_GetLineNumber(f); } + /* During Python finalization, warnings may be emited after + * interp->sysdict is cleared: set module to None to ignore the warning. + * See issue #22898. */ + if (globals == NULL) { + *module = Py_None; + Py_INCREF(*module); + *registry = NULL; + return 0; + } + *module = NULL; /* Setup registry. */ - assert(globals != NULL); assert(PyDict_Check(globals)); *registry = PyDict_GetItemString(globals, "__warningregistry__"); if (*registry == NULL) { @@ -665,7 +674,7 @@ res = warn_explicit(category, message, filename, lineno, module, registry, NULL); Py_DECREF(filename); - Py_DECREF(registry); + Py_XDECREF(registry); Py_DECREF(module); return res; } diff --git a/Python/errors.c b/Python/errors.c --- a/Python/errors.c +++ b/Python/errors.c @@ -312,20 +312,11 @@ Py_DECREF(initial_tb); } /* normalize recursively */ + /* The last of two consecutive RuntimeError caused by recursion is + * successfully instantiated and prevents the infinite recursion of + * PyErr_NormalizeException(). See issue 22898. */ tstate = PyThreadState_GET(); - if (++tstate->recursion_depth > Py_GetRecursionLimit()) { - --tstate->recursion_depth; - /* throw away the old exception... */ - Py_DECREF(*exc); - Py_DECREF(*val); - /* ... and use the recursion error instead */ - *exc = PyExc_RuntimeError; - *val = PyExc_RecursionErrorInst; - Py_INCREF(*exc); - Py_INCREF(*val); - /* just keeping the old traceback */ - return; - } + ++tstate->recursion_depth; PyErr_NormalizeException(exc, val, tb); --tstate->recursion_depth; }