https://github.com/python/cpython/commit/fd6b639a49dd1143c6fd8729fc49f17b3114a965
commit: fd6b639a49dd1143c6fd8729fc49f17b3114a965
branch: main
author: Peter Bierma <[email protected]>
committer: encukou <[email protected]>
date: 2026-02-09T16:57:03+01:00
summary:
gh-144601: Avoid sharing exception objects raised in a `PyInit` function across
multiple interpreters (GH-144602)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
M Lib/test/test_import/__init__.py
M Modules/_testsinglephase.c
M Python/import.c
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 59c6dc4587c93d..f66e2987d34850 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -45,6 +45,7 @@
Py_GIL_DISABLED,
no_rerun,
force_not_colorized_test_class,
+ catch_unraisable_exception
)
from test.support.import_helper import (
forget, make_legacy_pyc, unlink, unload, ready_to_import,
@@ -2517,6 +2518,32 @@ def test_disallowed_reimport(self):
excsnap = _interpreters.run_string(interpid, script)
self.assertIsNot(excsnap, None)
+ @requires_subinterpreters
+ def test_pyinit_function_raises_exception(self):
+ # gh-144601: PyInit functions that raised exceptions would cause a
+ # crash when imported from a subinterpreter.
+ import _testsinglephase
+ filename = _testsinglephase.__file__
+ script = f"""if True:
+ from test.test_import import import_extension_from_file
+
+ import_extension_from_file('_testsinglephase_raise_exception',
{filename!r})"""
+
+ interp = _interpreters.create()
+ try:
+ with catch_unraisable_exception() as cm:
+ exception = _interpreters.run_string(interp, script)
+ unraisable = cm.unraisable
+ finally:
+ _interpreters.destroy(interp)
+
+ self.assertIsNotNone(exception)
+ self.assertIsNotNone(exception.type.__name__, "ImportError")
+ self.assertIsNotNone(exception.msg, "failed to import from
subinterpreter due to exception")
+ self.assertIsNotNone(unraisable)
+ self.assertIs(unraisable.exc_type, RuntimeError)
+ self.assertEqual(str(unraisable.exc_value), "evil")
+
class TestSinglePhaseSnapshot(ModuleSnapshot):
"""A representation of a single-phase init module for testing.
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
new file mode 100644
index 00000000000000..1c7772e2f3ca26
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-08-12-47-27.gh-issue-144601.E4Yi9J.rst
@@ -0,0 +1,2 @@
+Fix crash when importing a module whose ``PyInit`` function raises an
+exception from a subinterpreter.
diff --git a/Modules/_testsinglephase.c b/Modules/_testsinglephase.c
index ee38d61b43a82a..7ea77c6312c59e 100644
--- a/Modules/_testsinglephase.c
+++ b/Modules/_testsinglephase.c
@@ -801,3 +801,11 @@ PyInit__testsinglephase_circular(void)
}
return Py_NewRef(static_module_circular);
}
+
+
+PyMODINIT_FUNC
+PyInit__testsinglephase_raise_exception(void)
+{
+ PyErr_SetString(PyExc_RuntimeError, "evil");
+ return NULL;
+}
diff --git a/Python/import.c b/Python/import.c
index 466c5868ab7ee8..e7f803d84f1367 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -2176,13 +2176,29 @@ import_run_extension(PyThreadState *tstate,
PyModInitFunction p0,
}
main_finally:
+ if (rc < 0) {
+ _Py_ext_module_loader_result_apply_error(&res, name_buf);
+ }
+
/* Switch back to the subinterpreter. */
if (switched) {
+ // gh-144601: The exception object can't be transferred across
+ // interpreters. Instead, we print out an unraisable exception, and
+ // then raise a different exception for the calling interpreter.
+ if (rc < 0) {
+ assert(PyErr_Occurred());
+ PyErr_FormatUnraisable("Exception while importing from
subinterpreter");
+ }
assert(main_tstate != tstate);
switch_back_from_main_interpreter(tstate, main_tstate, mod);
/* Any module we got from the init function will have to be
* reloaded in the subinterpreter. */
mod = NULL;
+ if (rc < 0) {
+ PyErr_SetString(PyExc_ImportError,
+ "failed to import from subinterpreter due to
exception");
+ goto error;
+ }
}
/*****************************************************************/
@@ -2191,7 +2207,6 @@ import_run_extension(PyThreadState *tstate,
PyModInitFunction p0,
/* Finally we handle the error return from _PyImport_RunModInitFunc(). */
if (rc < 0) {
- _Py_ext_module_loader_result_apply_error(&res, name_buf);
goto error;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]