From 69e82f2a8290356190a77f5ede09557ec4b640f5 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Tue, 4 Aug 2026 11:54:46 -0700 Subject: [PATCH 1/2] Fix not raising on non-module import --- Lib/test/test_lazy_import/__init__.py | 20 +++++++++++++--- Python/import.c | 34 ++++----------------------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py index 0a53d2559c91f0..f7386354fc8b43 100644 --- a/Lib/test/test_lazy_import/__init__.py +++ b/Lib/test/test_lazy_import/__init__.py @@ -686,17 +686,31 @@ def test_lazy_modules_tracks_lazy_imports(self): class ErrorHandlingTests(LazyImportTestCase): """Tests for error handling during lazy import reification.""" - def test_missing_lazy_submodule_raises_attribute_error(self): + def test_missing_lazy_submodule_raises_module_not_found_error(self): """Accessing a nonexistent lazy submodule via parent attr raises AttributeError.""" code = textwrap.dedent(""" lazy import test.test_lazy_import.data.nonexistent_module try: _ = test.test_lazy_import.data.nonexistent_module - except AttributeError: + except ModuleNotFoundError: pass else: - raise AssertionError("AttributeError was not raised") + raise AssertionError("ModuleNotFoundError was not raised") + """) + assert_python_ok("-c", code) + + def test_non_package_lazily_imported(self): + """Accessing a nonexistent lazy submodule via parent attr raises AttributeError.""" + code = textwrap.dedent(""" + lazy import math.pi + + try: + _ = math.pi + except ModuleNotFoundError: + pass + else: + raise AssertionError("ModuleNotFoundError was not raised") """) assert_python_ok("-c", code) diff --git a/Python/import.c b/Python/import.c index 287f5f611f5534..95f5c20bf4894a 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3937,19 +3937,6 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) goto error; } - Py_ssize_t dot = -1; - int full = 0; - if (lz->lz_attr != NULL) { - full = 1; - } - if (!full) { - dot = PyUnicode_FindChar(lz->lz_from, '.', 0, - PyUnicode_GET_LENGTH(lz->lz_from), 1); - } - if (dot < 0) { - full = 1; - } - if (lz->lz_attr != NULL) { if (PyUnicode_Check(lz->lz_attr)) { fromlist = PyTuple_New(1); @@ -3975,23 +3962,10 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import) PyErr_SetString(PyExc_ImportError, "__import__ not found"); goto error; } - if (full) { - obj = _PyEval_ImportNameWithImport( - tstate, import_func, globals, globals, - lz->lz_from, fromlist, _PyLong_GetZero() - ); - } - else { - PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot); - if (name == NULL) { - goto error; - } - obj = _PyEval_ImportNameWithImport( - tstate, import_func, globals, globals, - name, fromlist, _PyLong_GetZero() - ); - Py_DECREF(name); - } + obj = _PyEval_ImportNameWithImport( + tstate, import_func, globals, globals, + lz->lz_from, fromlist, _PyLong_GetZero() + ); if (obj == NULL) { goto error; } From 7063fa2cb3a63df6c6ac6d50723d37fd796a41d6 Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Tue, 4 Aug 2026 14:14:06 -0700 Subject: [PATCH 2/2] Fix traceback tests --- Lib/test/test_lazy_import/data/lazypkg/__init__.py | 1 + Lib/test/test_lazy_import/data/lazypkg/bar.py | 5 +++++ Lib/test/test_traceback.py | 14 +++++++------- Makefile.pre.in | 1 + 4 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 Lib/test/test_lazy_import/data/lazypkg/__init__.py create mode 100644 Lib/test/test_lazy_import/data/lazypkg/bar.py diff --git a/Lib/test/test_lazy_import/data/lazypkg/__init__.py b/Lib/test/test_lazy_import/data/lazypkg/__init__.py new file mode 100644 index 00000000000000..276b51823fee32 --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazypkg/__init__.py @@ -0,0 +1 @@ +lazy from . import bar diff --git a/Lib/test/test_lazy_import/data/lazypkg/bar.py b/Lib/test/test_lazy_import/data/lazypkg/bar.py new file mode 100644 index 00000000000000..bd5e116e7047bc --- /dev/null +++ b/Lib/test/test_lazy_import/data/lazypkg/bar.py @@ -0,0 +1,5 @@ +import traceback +traceback.print_stack() +while True: pass +print("BAR_MODULE_LOADED") +def f(): pass diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index bb64153b91c92c..6b4f1ee6de6c51 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -5596,11 +5596,11 @@ class TestLazyImportSuggestions(unittest.TestCase): def test_attribute_error_does_not_reify_lazy_imports(self): """Printing an AttributeError should not trigger lazy import reification.""" - # pkg.bar prints "BAR_MODULE_LOADED" when imported. + # lazypkg.bar prints "BAR_MODULE_LOADED" when imported. # If lazy import is reified during suggestion computation, we'll see it. code = textwrap.dedent(""" - lazy import test.test_lazy_import.data.pkg.bar - test.test_lazy_import.data.pkg.nonexistent + lazy import test.test_lazy_import.data.lazypkg + test.test_lazy_import.data.lazypkg.nonexistent """) rc, stdout, stderr = assert_python_failure('-c', code) self.assertNotIn(b"BAR_MODULE_LOADED", stdout) @@ -5609,9 +5609,9 @@ def test_traceback_formatting_does_not_reify_lazy_imports(self): """Formatting a traceback should not trigger lazy import reification.""" code = textwrap.dedent(""" import traceback - lazy import test.test_lazy_import.data.pkg.bar + lazy import test.test_lazy_import.data.lazypkg try: - test.test_lazy_import.data.pkg.nonexistent + test.test_lazy_import.data.lazypkg.nonexistent except AttributeError: traceback.format_exc() print("OK") @@ -5623,9 +5623,9 @@ def test_traceback_formatting_does_not_reify_lazy_imports(self): def test_suggestion_still_works_for_non_lazy_attributes(self): """Suggestions should still work for non-lazy module attributes.""" code = textwrap.dedent(""" - lazy import test.test_lazy_import.data.pkg.bar + lazy import test.test_lazy_import.data.lazypkg # Typo for __name__ - test.test_lazy_import.data.pkg.__nme__ + test.test_lazy_import.data.lazypkg.__nme__ """) rc, stdout, stderr = assert_python_failure('-c', code) self.assertIn(b"__name__", stderr) diff --git a/Makefile.pre.in b/Makefile.pre.in index 77dde715a4852d..1f4c366d30c44f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2772,6 +2772,7 @@ TESTSUBDIRS= idlelib/idle_test \ test/test_lazy_import/data \ test/test_lazy_import/data/pkg \ test/test_lazy_import/data/badsyntax \ + test/test_lazy_import/data/lazypkg \ test/test_module \ test/test_multiprocessing_fork \ test/test_multiprocessing_forkserver \