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
34 changes: 31 additions & 3 deletions Lib/test/test_lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,17 +678,45 @@ 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 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)

def test_missing_attribute_raises_import_error(self):
"""Accessing a nonexistent lazy submodule via from import raises ImportError."""
code = textwrap.dedent("""
lazy from sys import doesnotexist

try:
_ = doesnotexist
except ImportError:
pass
else:
raise AssertionError("ImportError 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 @@ -5611,11 +5611,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 @@ -5624,9 +5624,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 @@ -5638,9 +5638,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 @@ -2690,6 +2690,7 @@ TESTSUBDIRS= idlelib/idle_test \
test/test_lazy_import/data/pkg \
test/test_lazy_import/data/badsyntax \
test/test_lazy_import/data/circular_import_pkg \
test/test_lazy_import/data/lazypkg \
test/test_lazy_import/data/metasyntactic \
test/test_lazy_import/data/metasyntactic/foo \
test/test_lazy_import/data/metasyntactic/foo/ack \
Expand Down
34 changes: 4 additions & 30 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -3940,19 +3940,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 @@ -3978,23 +3965,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;
}
Expand Down
Loading