diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -5,7 +5,7 @@ import sys import unittest from test import support -from test.script_helper import assert_python_ok +from test.script_helper import assert_python_ok, assert_python_failure from test import warning_tests @@ -862,6 +862,32 @@ # of the script self.assertEqual(err, b'__main__:7: UserWarning: test') + def test_issue22898(self): + # Should not crash when interp->sysdict is NULL at the end of Python + # finalization. + code = """if 1: + import sys, traceback + + class MyException(Exception): + def __init__(self, *args): + 1/0 + + def gen(): + f = open('%s') + yield + + g = gen() + next(g) + recursionlimit = sys.getrecursionlimit() + sys.setrecursionlimit(len(traceback.extract_stack())) + try: + g.throw(MyException) + finally: + sys.setrecursionlimit(recursionlimit) + """ % __file__ + rc, out, err = assert_python_failure("-c", code) + # Check that the program does not fail with SIGSEV or SIGABRT. + self.assertEqual(rc, 1) def setUpModule(): py_warnings.onceregistry.clear() 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; }