Skip to content

Commit dba976b

Browse files
njsmith1st1
authored andcommitted
bpo-32591: fix abort in _PyErr_WarnUnawaitedCoroutine during shutdown (#5337)
When an unawaited coroutine is collected very late in shutdown -- like, during the final GC at the end of PyImport_Cleanup -- then it was triggering an interpreter abort, because we'd try to look up the "warnings" module and not only was it missing (we were prepared for that), but the entire module system was missing (which we were not prepared for). I've tried to fix this at the source, by making the utility function get_warnings_attr robust against this in general. Note that it already has the convention that it can return NULL without setting an error, which is how it signals that the attribute it was asked to fetch is missing, and that all callers already check for NULL returns. There's a similar check for being late in shutdown at the top of warn_explicit, which might be unnecessary after this fix, but I'm not sure so I'm going to leave it.
1 parent 95e4d58 commit dba976b

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

Lib/test/test_coroutines.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import unittest
99
import warnings
1010
from test import support
11+
from test.support.script_helper import assert_python_ok
1112

1213

1314
class AsyncYieldFrom:
@@ -2168,6 +2169,27 @@ async def corofn():
21682169
finally:
21692170
warnings._warn_unawaited_coroutine = orig_wuc
21702171

2172+
2173+
class UnawaitedWarningDuringShutdownTest(unittest.TestCase):
2174+
# https://bugs.python.org/issue32591#msg310726
2175+
def test_unawaited_warning_during_shutdown(self):
2176+
code = ("import asyncio\n"
2177+
"async def f(): pass\n"
2178+
"asyncio.gather(f())\n")
2179+
assert_python_ok("-c", code)
2180+
2181+
code = ("import sys\n"
2182+
"async def f(): pass\n"
2183+
"sys.coro = f()\n")
2184+
assert_python_ok("-c", code)
2185+
2186+
code = ("import sys\n"
2187+
"async def f(): pass\n"
2188+
"sys.corocycle = [f()]\n"
2189+
"sys.corocycle.append(sys.corocycle)\n")
2190+
assert_python_ok("-c", code)
2191+
2192+
21712193
@support.cpython_only
21722194
class CAPITest(unittest.TestCase):
21732195

Python/_warnings.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import)
7575
}
7676
}
7777
else {
78+
/* if we're so late into Python finalization that the module dict is
79+
gone, then we can't even use PyImport_GetModule without triggering
80+
an interpreter abort.
81+
*/
82+
if (!PyThreadState_GET()->interp->modules) {
83+
return NULL;
84+
}
7885
warnings_module = PyImport_GetModule(warnings_str);
7986
if (warnings_module == NULL)
8087
return NULL;

0 commit comments

Comments
 (0)