diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 4eecec588be..cff4664fa1e 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -2558,6 +2558,26 @@ def test_basic_unused(self): self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules) + def test_basic_unused_use_externally(self): + try: + from test.test_import.data.lazy_imports import basic_unused + except ImportError as e: + self.fail('lazy import failed') + + self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules) + x = basic_unused.test.test_import.data.lazy_imports.basic2 + self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules) + + def test_basic_from_unused_use_externally(self): + try: + from test.test_import.data.lazy_imports import basic_from_unused + except ImportError as e: + self.fail('lazy import failed') + + self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules) + x = basic_from_unused.basic2 + self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules) + def test_basic_unused_dir(self): try: import test.test_import.data.lazy_imports.basic_unused @@ -2758,6 +2778,19 @@ def test_lazy_import_pkg(self): self.assertTrue("test.test_import.data.lazy_imports.pkg" in sys.modules) self.assertTrue("test.test_import.data.lazy_imports.pkg.bar" in sys.modules) + def test_lazy_import_pkg_cross_import(self): + try: + import test.test_import.data.lazy_imports.pkg.c + except ImportError as e: + self.fail('lazy import failed') + + self.assertTrue("test.test_import.data.lazy_imports.pkg" in sys.modules) + self.assertTrue("test.test_import.data.lazy_imports.pkg.c" in sys.modules) + self.assertFalse("test.test_import.data.lazy_imports.pkg.b" in sys.modules) + + g = test.test_import.data.lazy_imports.pkg.c.get_globals() + self.assertEqual(type(g["x"]), int) + self.assertEqual(type(g["b"]), importlib.lazy_import) class TestSinglePhaseSnapshot(ModuleSnapshot): """A representation of a single-phase init module for testing. diff --git a/Lib/test/test_import/data/lazy_imports/basic_from_unused.py b/Lib/test/test_import/data/lazy_imports/basic_from_unused.py new file mode 100644 index 00000000000..686caa86a6c --- /dev/null +++ b/Lib/test/test_import/data/lazy_imports/basic_from_unused.py @@ -0,0 +1 @@ +lazy from test.test_import.data.lazy_imports import basic2 diff --git a/Lib/test/test_import/data/lazy_imports/pkg/b.py b/Lib/test/test_import/data/lazy_imports/pkg/b.py new file mode 100644 index 00000000000..3aa53d31aae --- /dev/null +++ b/Lib/test/test_import/data/lazy_imports/pkg/b.py @@ -0,0 +1,3 @@ +def foo(): + return 'foo' + diff --git a/Lib/test/test_import/data/lazy_imports/pkg/c.py b/Lib/test/test_import/data/lazy_imports/pkg/c.py new file mode 100644 index 00000000000..0bb03119864 --- /dev/null +++ b/Lib/test/test_import/data/lazy_imports/pkg/c.py @@ -0,0 +1,4 @@ +lazy from . import b, x + +def get_globals(): + return globals() diff --git a/Python/ceval.c b/Python/ceval.c index 85835d99230..4a0929c6992 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3304,6 +3304,22 @@ _PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name) assert(name && PyUnicode_Check(name)); PyObject *ret; PyLazyImportObject *d = (PyLazyImportObject *)v; + PyObject *mod = PyImport_GetModule(d->lz_from); + if (mod != NULL) { + // Check if the module already has the attribute, if so, resolve it eagerly. + if (PyModule_Check(mod)) { + PyObject *mod_dict = PyModule_GetDict(mod); + if (mod_dict != NULL) { + if (PyDict_GetItemRef(mod_dict, name, &ret) < 0) { + return NULL; + } else if (ret != NULL) { + return ret; + } + } + } + Py_DECREF(mod); + } + if (d->lz_attr != NULL) { if (PyUnicode_Check(d->lz_attr)) { PyObject *from = PyUnicode_FromFormat("%U.%U", d->lz_from, d->lz_attr); diff --git a/Python/import.c b/Python/import.c index cf526acb345..3d5aa53ae70 100644 --- a/Python/import.c +++ b/Python/import.c @@ -4221,16 +4221,8 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, } PyInterpreterState *interp = tstate->interp; - _PyInterpreterFrame *frame = _PyEval_GetFrame(); assert(frame->f_globals == frame->f_locals); // should only be called in global scope - PyObject *mod = PyImport_GetModule(abs_name); - bool already_exists = mod != NULL; - Py_XDECREF(mod); - if (mod != NULL) { - return PyImport_ImportModuleLevelObject(name, globals, locals, fromlist, level); - } - // Check if the filter disables the lazy import PyObject *filter = LAZY_IMPORTS_FILTER(interp); if (filter != NULL) { @@ -4262,7 +4254,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, } PyObject *res = _PyLazyImport_New(import_func, abs_name, fromlist); - if (!already_exists && register_lazy_on_parent(tstate, abs_name, import_func) < 0) { + if (register_lazy_on_parent(tstate, abs_name, import_func) < 0) { Py_DECREF(res); res = NULL; }