Skip to content

Commit 05f4932

Browse files
Merge branch 'main' into traceback-to-handle-error-msgs-with-period
2 parents ccf70c1 + fd190d1 commit 05f4932

6 files changed

Lines changed: 59 additions & 5 deletions

File tree

Lib/test/test_cppext/extension.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@
1616
// gh-135906: Check for compiler warnings in the internal C API
1717
# include "internal/pycore_frame.h"
1818
// mimalloc emits many compiler warnings when Python is built in debug
19-
// mode (when MI_DEBUG is not zero)
20-
// mimalloc emits compiler warnings when Python is built on Windows
21-
// in free-threaded mode.
22-
# if !defined(Py_DEBUG) && !(defined(MS_WINDOWS) && defined(Py_GIL_DISABLED))
19+
// mode (when MI_DEBUG is not zero).
20+
// mimalloc emits compiler warnings when Python is built on Windows.
21+
# if !defined(Py_DEBUG) && !defined(MS_WINDOWS)
2322
# include "internal/pycore_backoff.h"
2423
# include "internal/pycore_cell.h"
2524
# endif

Lib/test/test_import/__init__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
Py_GIL_DISABLED,
4646
no_rerun,
4747
force_not_colorized_test_class,
48+
catch_unraisable_exception
4849
)
4950
from test.support.import_helper import (
5051
forget, make_legacy_pyc, unlink, unload, ready_to_import,
@@ -2517,6 +2518,32 @@ def test_disallowed_reimport(self):
25172518
excsnap = _interpreters.run_string(interpid, script)
25182519
self.assertIsNot(excsnap, None)
25192520

2521+
@requires_subinterpreters
2522+
def test_pyinit_function_raises_exception(self):
2523+
# gh-144601: PyInit functions that raised exceptions would cause a
2524+
# crash when imported from a subinterpreter.
2525+
import _testsinglephase
2526+
filename = _testsinglephase.__file__
2527+
script = f"""if True:
2528+
from test.test_import import import_extension_from_file
2529+
2530+
import_extension_from_file('_testsinglephase_raise_exception', {filename!r})"""
2531+
2532+
interp = _interpreters.create()
2533+
try:
2534+
with catch_unraisable_exception() as cm:
2535+
exception = _interpreters.run_string(interp, script)
2536+
unraisable = cm.unraisable
2537+
finally:
2538+
_interpreters.destroy(interp)
2539+
2540+
self.assertIsNotNone(exception)
2541+
self.assertIsNotNone(exception.type.__name__, "ImportError")
2542+
self.assertIsNotNone(exception.msg, "failed to import from subinterpreter due to exception")
2543+
self.assertIsNotNone(unraisable)
2544+
self.assertIs(unraisable.exc_type, RuntimeError)
2545+
self.assertEqual(str(unraisable.exc_value), "evil")
2546+
25202547

25212548
class TestSinglePhaseSnapshot(ModuleSnapshot):
25222549
"""A representation of a single-phase init module for testing.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when importing a module whose ``PyInit`` function raises an
2+
exception from a subinterpreter.

Modules/_testsinglephase.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,3 +801,11 @@ PyInit__testsinglephase_circular(void)
801801
}
802802
return Py_NewRef(static_module_circular);
803803
}
804+
805+
806+
PyMODINIT_FUNC
807+
PyInit__testsinglephase_raise_exception(void)
808+
{
809+
PyErr_SetString(PyExc_RuntimeError, "evil");
810+
return NULL;
811+
}

Python/import.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2176,13 +2176,29 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
21762176
}
21772177

21782178
main_finally:
2179+
if (rc < 0) {
2180+
_Py_ext_module_loader_result_apply_error(&res, name_buf);
2181+
}
2182+
21792183
/* Switch back to the subinterpreter. */
21802184
if (switched) {
2185+
// gh-144601: The exception object can't be transferred across
2186+
// interpreters. Instead, we print out an unraisable exception, and
2187+
// then raise a different exception for the calling interpreter.
2188+
if (rc < 0) {
2189+
assert(PyErr_Occurred());
2190+
PyErr_FormatUnraisable("Exception while importing from subinterpreter");
2191+
}
21812192
assert(main_tstate != tstate);
21822193
switch_back_from_main_interpreter(tstate, main_tstate, mod);
21832194
/* Any module we got from the init function will have to be
21842195
* reloaded in the subinterpreter. */
21852196
mod = NULL;
2197+
if (rc < 0) {
2198+
PyErr_SetString(PyExc_ImportError,
2199+
"failed to import from subinterpreter due to exception");
2200+
goto error;
2201+
}
21862202
}
21872203

21882204
/*****************************************************************/
@@ -2191,7 +2207,6 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
21912207

21922208
/* Finally we handle the error return from _PyImport_RunModInitFunc(). */
21932209
if (rc < 0) {
2194-
_Py_ext_module_loader_result_apply_error(&res, name_buf);
21952210
goto error;
21962211
}
21972212

Tools/build/compute-changes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,12 @@ def process_changed_files(changed_files: Set[Path]) -> Outputs:
232232
if file.name == "reusable-windows-msi.yml":
233233
run_windows_msi = True
234234
if file.name == "reusable-macos.yml":
235+
run_tests = True
235236
platforms_changed.add("macos")
236237
if file.name == "reusable-wasi.yml":
238+
run_tests = True
237239
platforms_changed.add("wasi")
240+
continue
238241

239242
if not doc_file and file not in RUN_TESTS_IGNORE:
240243
run_tests = True

0 commit comments

Comments
 (0)