summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel J. Smith <njs@pobox.com>2018-01-26 19:28:31 (GMT)
committerYury Selivanov <yury@magic.io>2018-01-26 19:28:31 (GMT)
commitdba976b8a28d6e5daa66ef31a6a7c694a9193f6a (patch)
treef43c9543141ae14fb1909d8368689c819ceb25ab
parent95e4d589137260530e18ef98a2ed84ee3ec57e12 (diff)
downloadcpython-dba976b8a28d6e5daa66ef31a6a7c694a9193f6a.zip
cpython-dba976b8a28d6e5daa66ef31a6a7c694a9193f6a.tar.gz
cpython-dba976b8a28d6e5daa66ef31a6a7c694a9193f6a.tar.bz2
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.
-rw-r--r--Lib/test/test_coroutines.py22
-rw-r--r--Python/_warnings.c7
2 files changed, 29 insertions, 0 deletions
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 8a531b8..a97535a 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -8,6 +8,7 @@ import types
import unittest
import warnings
from test import support
+from test.support.script_helper import assert_python_ok
class AsyncYieldFrom:
@@ -2168,6 +2169,27 @@ class OriginTrackingTest(unittest.TestCase):
finally:
warnings._warn_unawaited_coroutine = orig_wuc
+
+class UnawaitedWarningDuringShutdownTest(unittest.TestCase):
+ # https://bugs.python.org/issue32591#msg310726
+ def test_unawaited_warning_during_shutdown(self):
+ code = ("import asyncio\n"
+ "async def f(): pass\n"
+ "asyncio.gather(f())\n")
+ assert_python_ok("-c", code)
+
+ code = ("import sys\n"
+ "async def f(): pass\n"
+ "sys.coro = f()\n")
+ assert_python_ok("-c", code)
+
+ code = ("import sys\n"
+ "async def f(): pass\n"
+ "sys.corocycle = [f()]\n"
+ "sys.corocycle.append(sys.corocycle)\n")
+ assert_python_ok("-c", code)
+
+
@support.cpython_only
class CAPITest(unittest.TestCase):
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 16ae932..0568af4 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -75,6 +75,13 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import)
}
}
else {
+ /* if we're so late into Python finalization that the module dict is
+ gone, then we can't even use PyImport_GetModule without triggering
+ an interpreter abort.
+ */
+ if (!PyThreadState_GET()->interp->modules) {
+ return NULL;
+ }
warnings_module = PyImport_GetModule(warnings_str);
if (warnings_module == NULL)
return NULL;