Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions Lib/test/test_lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Accessing a nonexistent lazy submodule via parent attr raises AttributeError."""
"""Accessing a nonexistent lazy submodule via parent attr raises ModuleNotFoundError."""

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)

Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_lazy_import/data/lazypkg/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lazy from . import bar
5 changes: 5 additions & 0 deletions Lib/test/test_lazy_import/data/lazypkg/bar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import traceback
traceback.print_stack()
while True: pass
print("BAR_MODULE_LOADED")
def f(): pass
14 changes: 7 additions & 7 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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")
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
34 changes: 4 additions & 30 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, does this change the error for lazy from ... import nonexistent_attr? We don't seem to have explicit tests for what that raises, just the chaining test?

tstate, import_func, globals, globals,
lz->lz_from, fromlist, _PyLong_GetZero()
);
if (obj == NULL) {
goto error;
}
Expand Down
Loading