From e85b222542eb746f13e243575e743f37f17b1c3a Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Sat, 29 Nov 2025 07:24:05 +1030 Subject: [PATCH 1/6] Don't assume that all non-found modules in `create_builtin()` are `sys`/`builtin` --- Python/import.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Python/import.c b/Python/import.c index e91c95b40d94bf..287f2e33154515 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2435,9 +2435,14 @@ create_builtin( p0 = lookup_inittab_initfunc(&info); if (p0 == NULL) { /* Cannot re-init internal module ("sys" or "builtins") */ - assert(is_core_module(tstate->interp, info.name, info.path)); - mod = import_add_module(tstate, info.name); - goto finally; + if (is_core_module(tstate->interp, info.name, info.path)) { + mod = import_add_module(tstate, info.name); + goto finally; + } + else { + mod = Py_NewRef(Py_None); + goto finally; + } } } From 68e2b7eaf24fd71ca23826bb95e2d2b7f7f6776e Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Sat, 29 Nov 2025 08:49:06 +1030 Subject: [PATCH 2/6] Add tests for internal and nonexistent modules passed to `create_builtin()` --- Lib/test/test_import/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 271361ae816449..a2631191b75495 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1253,6 +1253,19 @@ class Spec2: origin = "a\x00b" _imp.create_dynamic(Spec2()) + def test_create_builtin(self): + for internal_mod in (sys, builtins): + class Spec: + name = internal_mod.__name__ + + self.assertIs(_imp.create_builtin(Spec()), internal_mod) + + class Spec: + name = "nonexistent_lib" + + # gh-142029 + self.assertIs(_imp.create_builtin(Spec()), None) + def test_filter_syntax_warnings_by_module(self): module_re = r'test\.test_import\.data\.syntax_warnings\z' unload('test.test_import.data.syntax_warnings') From 603d98b354464f3ace1c660237c5f90f16b48c40 Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Sat, 29 Nov 2025 08:52:21 +1030 Subject: [PATCH 3/6] Add NEWS entry --- .../2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst new file mode 100644 index 00000000000000..959f81d66197b8 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst @@ -0,0 +1,2 @@ +Return ``None`` instead of crashing when a nonexistent module is used as a +name in ``_imp.create_builtin()``. From d45c2d90f9d243e405af9a97e16bc42a443100e9 Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Mon, 1 Dec 2025 11:52:42 +1030 Subject: [PATCH 4/6] Properly handle `inittab` entries with no `initfunc` --- Python/import.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/Python/import.c b/Python/import.c index 287f2e33154515..d8d11fae2302e8 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2383,12 +2383,12 @@ is_builtin(PyObject *name) return 0; } -static PyModInitFunction -lookup_inittab_initfunc(const struct _Py_ext_module_loader_info* info) +static struct _inittab* +lookup_inittab_entry(const struct _Py_ext_module_loader_info* info) { for (struct _inittab *p = INITTAB; p->name != NULL; p++) { if (_PyUnicode_EqualToASCIIString(info->name, p->name)) { - return (PyModInitFunction)p->initfunc; + return p; } } // not found @@ -2430,22 +2430,28 @@ create_builtin( _extensions_cache_delete(info.path, info.name); } - PyModInitFunction p0 = initfunc; - if (p0 == NULL) { - p0 = lookup_inittab_initfunc(&info); - if (p0 == NULL) { - /* Cannot re-init internal module ("sys" or "builtins") */ - if (is_core_module(tstate->interp, info.name, info.path)) { - mod = import_add_module(tstate, info.name); - goto finally; - } - else { - mod = Py_NewRef(Py_None); - goto finally; - } + PyModInitFunction p0 = NULL; + if (initfunc == NULL) { + struct _inittab *entry = lookup_inittab_entry(&info); + if (entry == NULL) { + mod = Py_NewRef(Py_None); + goto finally; } + + p0 = (PyModInitFunction)entry->initfunc; + } + else { + p0 = initfunc; + } + + if (p0 == NULL) { + /* Cannot re-init internal module ("sys" or "builtins") */ + assert(is_core_module(tstate->interp, info.name, info.path)); + mod = import_add_module(tstate, info.name); + goto finally; } + #ifdef Py_GIL_DISABLED // This call (and the corresponding call to _PyImport_CheckGILForModule()) // would ideally be inside import_run_extension(). They are kept in the From d07443c1f7e3638bb93ef02ebb9d23522a1ea3b8 Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Wed, 3 Dec 2025 09:09:35 +1030 Subject: [PATCH 5/6] Raise `ModuleNotFoundError` instead of returning `None` --- Lib/test/test_import/__init__.py | 16 +++++++++------- Python/import.c | 3 ++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index a2631191b75495..63cbbfd899a961 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1254,17 +1254,19 @@ class Spec2: _imp.create_dynamic(Spec2()) def test_create_builtin(self): - for internal_mod in (sys, builtins): - class Spec: - name = internal_mod.__name__ + class Spec: + name = "sys" - self.assertIs(_imp.create_builtin(Spec()), internal_mod) + spec = Spec() + self.assertIs(_imp.create_builtin(spec), sys) - class Spec: - name = "nonexistent_lib" + spec.name = "builtins" + self.assertIs(_imp.create_builtin(spec), builtins) # gh-142029 - self.assertIs(_imp.create_builtin(Spec()), None) + spec.name = "nonexistent_lib" + with self.assertRaises(ModuleNotFoundError): + _imp.create_builtin(spec) def test_filter_syntax_warnings_by_module(self): module_re = r'test\.test_import\.data\.syntax_warnings\z' diff --git a/Python/import.c b/Python/import.c index d8d11fae2302e8..6c9f49842d4e94 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2434,7 +2434,8 @@ create_builtin( if (initfunc == NULL) { struct _inittab *entry = lookup_inittab_entry(&info); if (entry == NULL) { - mod = Py_NewRef(Py_None); + mod = NULL; + _PyErr_SetModuleNotFoundError(name); goto finally; } From 5c67d47d8761c170c0a5cd32f78b3e56c95dd48f Mon Sep 17 00:00:00 2001 From: dr-carlos Date: Wed, 3 Dec 2025 09:10:38 +1030 Subject: [PATCH 6/6] Update NEWS entry --- .../2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst index 959f81d66197b8..017761adb1255e 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-29-08-51-56.gh-issue-142029.rUpcmt.rst @@ -1,2 +1,2 @@ -Return ``None`` instead of crashing when a nonexistent module is used as a -name in ``_imp.create_builtin()``. +Raise :exc:`ModuleNotFoundError` instead of crashing when a nonexistent module +is used as a name in ``_imp.create_builtin()``.