https://github.com/python/cpython/commit/9314ec23a6b22758ecc0f43709b9fc49b253fb90
commit: 9314ec23a6b22758ecc0f43709b9fc49b253fb90
branch: 3.13
author: Peter Bierma <[email protected]>
committer: ZeroIntensity <[email protected]>
date: 2026-02-16T16:05:55Z
summary:

[3.13] gh-144601: Avoid sharing exception objects raised in a `PyInit` function 
across multiple interpreters (GH-144602) (GH-144880)

(cherry picked from commit fd6b639a49dd1143c6fd8729fc49f17b3114a965)

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 8f3fd8ec2c35f9..4d2c263bebd272 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -42,6 +42,7 @@
     requires_gil_enabled,
     Py_GIL_DISABLED,
     force_not_colorized_test_class,
+    catch_unraisable_exception
 )
 from test.support.import_helper import (
     forget, make_legacy_pyc, unlink, unload, ready_to_import,
@@ -2559,6 +2560,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 2c59085d15b5be..f74b964faf35fb 100644
--- a/Modules/_testsinglephase.c
+++ b/Modules/_testsinglephase.c
@@ -799,3 +799,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 eb955d194745ff..f34e32c95828fd 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -2089,13 +2089,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;
+        }
     }
 
     /*****************************************************************/
@@ -2104,7 +2120,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]

Reply via email to