Skip to content

Commit dffac61

Browse files
authored
gh-155194: Fix not raising on non-module import (#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
1 parent bc6749c commit dffac61

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
@@ -678,17 +678,53 @@ def test_lazy_modules_tracks_lazy_imports(self):
678678
class ErrorHandlingTests(LazyImportTestCase):
679679
"""Tests for error handling during lazy import reification."""
680680

681-
def test_missing_lazy_submodule_raises_attribute_error(self):
682-
"""Accessing a nonexistent lazy submodule via parent attr raises AttributeError."""
681+
def test_missing_lazy_submodule_raises_module_not_found_error(self):
682+
"""Accessing a nonexistent lazy submodule via parent attr raises ModuleNotFoundError."""
683683
code = textwrap.dedent("""
684684
lazy import test.test_lazy_import.data.nonexistent_module
685685
686686
try:
687687
_ = test.test_lazy_import.data.nonexistent_module
688-
except AttributeError:
688+
except ModuleNotFoundError:
689689
pass
690690
else:
691-
raise AssertionError("AttributeError was not raised")
691+
raise AssertionError("ModuleNotFoundError was not raised")
692+
""")
693+
assert_python_ok("-c", code)
694+
695+
def test_non_package_lazily_imported(self):
696+
"""Accessing a nonexistent lazy name via parent attr raises ModuleNotFoundError."""
697+
code = textwrap.dedent("""
698+
lazy import math.pi
699+
700+
try:
701+
_ = math.pi
702+
except ModuleNotFoundError:
703+
pass
704+
else:
705+
raise AssertionError("ModuleNotFoundError was not raised")
706+
""")
707+
assert_python_ok("-c", code)
708+
709+
def test_non_package_lazily_imported_as(self):
710+
"""Doing a dotted lazy import as still works"""
711+
code = textwrap.dedent("""
712+
lazy import math.pi as pi
713+
pi
714+
""")
715+
assert_python_ok("-c", code)
716+
717+
def test_missing_attribute_raises_import_error(self):
718+
"""Accessing a nonexistent lazy name via from import raises ImportError."""
719+
code = textwrap.dedent("""
720+
lazy from sys import doesnotexist
721+
722+
try:
723+
_ = doesnotexist
724+
except ImportError:
725+
pass
726+
else:
727+
raise AssertionError("ImportError was not raised")
692728
""")
693729
assert_python_ok("-c", code)
694730

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
@@ -5611,11 +5611,11 @@ class TestLazyImportSuggestions(unittest.TestCase):
56115611

56125612
def test_attribute_error_does_not_reify_lazy_imports(self):
56135613
"""Printing an AttributeError should not trigger lazy import reification."""
5614-
# pkg.bar prints "BAR_MODULE_LOADED" when imported.
5614+
# lazypkg.bar prints "BAR_MODULE_LOADED" when imported.
56155615
# If lazy import is reified during suggestion computation, we'll see it.
56165616
code = textwrap.dedent("""
5617-
lazy import test.test_lazy_import.data.pkg.bar
5618-
test.test_lazy_import.data.pkg.nonexistent
5617+
lazy import test.test_lazy_import.data.lazypkg
5618+
test.test_lazy_import.data.lazypkg.nonexistent
56195619
""")
56205620
rc, stdout, stderr = assert_python_failure('-c', code)
56215621
self.assertNotIn(b"BAR_MODULE_LOADED", stdout)
@@ -5624,9 +5624,9 @@ def test_traceback_formatting_does_not_reify_lazy_imports(self):
56245624
"""Formatting a traceback should not trigger lazy import reification."""
56255625
code = textwrap.dedent("""
56265626
import traceback
5627-
lazy import test.test_lazy_import.data.pkg.bar
5627+
lazy import test.test_lazy_import.data.lazypkg
56285628
try:
5629-
test.test_lazy_import.data.pkg.nonexistent
5629+
test.test_lazy_import.data.lazypkg.nonexistent
56305630
except AttributeError:
56315631
traceback.format_exc()
56325632
print("OK")
@@ -5638,9 +5638,9 @@ def test_traceback_formatting_does_not_reify_lazy_imports(self):
56385638
def test_suggestion_still_works_for_non_lazy_attributes(self):
56395639
"""Suggestions should still work for non-lazy module attributes."""
56405640
code = textwrap.dedent("""
5641-
lazy import test.test_lazy_import.data.pkg.bar
5641+
lazy import test.test_lazy_import.data.lazypkg
56425642
# Typo for __name__
5643-
test.test_lazy_import.data.pkg.__nme__
5643+
test.test_lazy_import.data.lazypkg.__nme__
56445644
""")
56455645
rc, stdout, stderr = assert_python_failure('-c', code)
56465646
self.assertIn(b"__name__", stderr)

Makefile.pre.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,7 @@ TESTSUBDIRS= idlelib/idle_test \
26902690
test/test_lazy_import/data/pkg \
26912691
test/test_lazy_import/data/badsyntax \
26922692
test/test_lazy_import/data/circular_import_pkg \
2693+
test/test_lazy_import/data/lazypkg \
26932694
test/test_lazy_import/data/metasyntactic \
26942695
test/test_lazy_import/data/metasyntactic/foo \
26952696
test/test_lazy_import/data/metasyntactic/foo/ack \

Python/import.c

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,19 +3940,6 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
39403940
goto error;
39413941
}
39423942

3943-
Py_ssize_t dot = -1;
3944-
int full = 0;
3945-
if (lz->lz_attr != NULL) {
3946-
full = 1;
3947-
}
3948-
if (!full) {
3949-
dot = PyUnicode_FindChar(lz->lz_from, '.', 0,
3950-
PyUnicode_GET_LENGTH(lz->lz_from), 1);
3951-
}
3952-
if (dot < 0) {
3953-
full = 1;
3954-
}
3955-
39563943
if (lz->lz_attr != NULL) {
39573944
if (PyUnicode_Check(lz->lz_attr)) {
39583945
fromlist = PyTuple_New(1);
@@ -3978,23 +3965,10 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
39783965
PyErr_SetString(PyExc_ImportError, "__import__ not found");
39793966
goto error;
39803967
}
3981-
if (full) {
3982-
obj = _PyEval_ImportNameWithImport(
3983-
tstate, import_func, globals, globals,
3984-
lz->lz_from, fromlist, _PyLong_GetZero()
3985-
);
3986-
}
3987-
else {
3988-
PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot);
3989-
if (name == NULL) {
3990-
goto error;
3991-
}
3992-
obj = _PyEval_ImportNameWithImport(
3993-
tstate, import_func, globals, globals,
3994-
name, fromlist, _PyLong_GetZero()
3995-
);
3996-
Py_DECREF(name);
3997-
}
3968+
obj = _PyEval_ImportNameWithImport(
3969+
tstate, import_func, globals, globals,
3970+
lz->lz_from, fromlist, _PyLong_GetZero()
3971+
);
39983972
if (obj == NULL) {
39993973
goto error;
40003974
}

0 commit comments

Comments
 (0)