Skip to content

Commit a70b440

Browse files
DinoVbrittanyrey
authored andcommitted
[3.15] gh-155194: Fix not raising on non-module import (GH-155189)
* Fix not raising on non-module import * Fix traceback tests * Fix doc string and add new test * Fix up feedback on tests * Add test case for lazy import dotted.name as name (cherry picked from commit dffac61) Co-authored-by: Dino Viehland <dinoviehland@meta.com>
1 parent ac4e5d2 commit a70b440

6 files changed

Lines changed: 55 additions & 41 deletions

File tree

Lib/test/test_lazy_import/__init__.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -686,17 +686,53 @@ def test_lazy_modules_tracks_lazy_imports(self):
686686
class ErrorHandlingTests(LazyImportTestCase):
687687
"""Tests for error handling during lazy import reification."""
688688

689-
def test_missing_lazy_submodule_raises_attribute_error(self):
690-
"""Accessing a nonexistent lazy submodule via parent attr raises AttributeError."""
689+
def test_missing_lazy_submodule_raises_module_not_found_error(self):
690+
"""Accessing a nonexistent lazy submodule via parent attr raises ModuleNotFoundError."""
691691
code = textwrap.dedent("""
692692
lazy import test.test_lazy_import.data.nonexistent_module
693693
694694
try:
695695
_ = test.test_lazy_import.data.nonexistent_module
696-
except AttributeError:
696+
except ModuleNotFoundError:
697697
pass
698698
else:
699-
raise AssertionError("AttributeError was not raised")
699+
raise AssertionError("ModuleNotFoundError was not raised")
700+
""")
701+
assert_python_ok("-c", code)
702+
703+
def test_non_package_lazily_imported(self):
704+
"""Accessing a nonexistent lazy name via parent attr raises ModuleNotFoundError."""
705+
code = textwrap.dedent("""
706+
lazy import math.pi
707+
708+
try:
709+
_ = math.pi
710+
except ModuleNotFoundError:
711+
pass
712+
else:
713+
raise AssertionError("ModuleNotFoundError was not raised")
714+
""")
715+
assert_python_ok("-c", code)
716+
717+
def test_non_package_lazily_imported_as(self):
718+
"""Doing a dotted lazy import as still works"""
719+
code = textwrap.dedent("""
720+
lazy import math.pi as pi
721+
pi
722+
""")
723+
assert_python_ok("-c", code)
724+
725+
def test_missing_attribute_raises_import_error(self):
726+
"""Accessing a nonexistent lazy name via from import raises ImportError."""
727+
code = textwrap.dedent("""
728+
lazy from sys import doesnotexist
729+
730+
try:
731+
_ = doesnotexist
732+
except ImportError:
733+
pass
734+
else:
735+
raise AssertionError("ImportError was not raised")
700736
""")
701737
assert_python_ok("-c", code)
702738

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lazy from . import bar
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
print("BAR_MODULE_LOADED")
2+
def f(): pass

Lib/test/test_traceback.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5596,11 +5596,11 @@ class TestLazyImportSuggestions(unittest.TestCase):
55965596

55975597
def test_attribute_error_does_not_reify_lazy_imports(self):
55985598
"""Printing an AttributeError should not trigger lazy import reification."""
5599-
# pkg.bar prints "BAR_MODULE_LOADED" when imported.
5599+
# lazypkg.bar prints "BAR_MODULE_LOADED" when imported.
56005600
# If lazy import is reified during suggestion computation, we'll see it.
56015601
code = textwrap.dedent("""
5602-
lazy import test.test_lazy_import.data.pkg.bar
5603-
test.test_lazy_import.data.pkg.nonexistent
5602+
lazy import test.test_lazy_import.data.lazypkg
5603+
test.test_lazy_import.data.lazypkg.nonexistent
56045604
""")
56055605
rc, stdout, stderr = assert_python_failure('-c', code)
56065606
self.assertNotIn(b"BAR_MODULE_LOADED", stdout)
@@ -5609,9 +5609,9 @@ def test_traceback_formatting_does_not_reify_lazy_imports(self):
56095609
"""Formatting a traceback should not trigger lazy import reification."""
56105610
code = textwrap.dedent("""
56115611
import traceback
5612-
lazy import test.test_lazy_import.data.pkg.bar
5612+
lazy import test.test_lazy_import.data.lazypkg
56135613
try:
5614-
test.test_lazy_import.data.pkg.nonexistent
5614+
test.test_lazy_import.data.lazypkg.nonexistent
56155615
except AttributeError:
56165616
traceback.format_exc()
56175617
print("OK")
@@ -5623,9 +5623,9 @@ def test_traceback_formatting_does_not_reify_lazy_imports(self):
56235623
def test_suggestion_still_works_for_non_lazy_attributes(self):
56245624
"""Suggestions should still work for non-lazy module attributes."""
56255625
code = textwrap.dedent("""
5626-
lazy import test.test_lazy_import.data.pkg.bar
5626+
lazy import test.test_lazy_import.data.lazypkg
56275627
# Typo for __name__
5628-
test.test_lazy_import.data.pkg.__nme__
5628+
test.test_lazy_import.data.lazypkg.__nme__
56295629
""")
56305630
rc, stdout, stderr = assert_python_failure('-c', code)
56315631
self.assertIn(b"__name__", stderr)

Makefile.pre.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2772,6 +2772,7 @@ TESTSUBDIRS= idlelib/idle_test \
27722772
test/test_lazy_import/data \
27732773
test/test_lazy_import/data/pkg \
27742774
test/test_lazy_import/data/badsyntax \
2775+
test/test_lazy_import/data/lazypkg \
27752776
test/test_module \
27762777
test/test_multiprocessing_fork \
27772778
test/test_multiprocessing_forkserver \

Python/import.c

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3937,19 +3937,6 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
39373937
goto error;
39383938
}
39393939

3940-
Py_ssize_t dot = -1;
3941-
int full = 0;
3942-
if (lz->lz_attr != NULL) {
3943-
full = 1;
3944-
}
3945-
if (!full) {
3946-
dot = PyUnicode_FindChar(lz->lz_from, '.', 0,
3947-
PyUnicode_GET_LENGTH(lz->lz_from), 1);
3948-
}
3949-
if (dot < 0) {
3950-
full = 1;
3951-
}
3952-
39533940
if (lz->lz_attr != NULL) {
39543941
if (PyUnicode_Check(lz->lz_attr)) {
39553942
fromlist = PyTuple_New(1);
@@ -3975,23 +3962,10 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
39753962
PyErr_SetString(PyExc_ImportError, "__import__ not found");
39763963
goto error;
39773964
}
3978-
if (full) {
3979-
obj = _PyEval_ImportNameWithImport(
3980-
tstate, import_func, globals, globals,
3981-
lz->lz_from, fromlist, _PyLong_GetZero()
3982-
);
3983-
}
3984-
else {
3985-
PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot);
3986-
if (name == NULL) {
3987-
goto error;
3988-
}
3989-
obj = _PyEval_ImportNameWithImport(
3990-
tstate, import_func, globals, globals,
3991-
name, fromlist, _PyLong_GetZero()
3992-
);
3993-
Py_DECREF(name);
3994-
}
3965+
obj = _PyEval_ImportNameWithImport(
3966+
tstate, import_func, globals, globals,
3967+
lz->lz_from, fromlist, _PyLong_GetZero()
3968+
);
39953969
if (obj == NULL) {
39963970
goto error;
39973971
}

0 commit comments

Comments
 (0)