diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index fbb64332f58..9e69e520d3d 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -34,6 +34,8 @@ extern int _PyImport_FixupBuiltin( extern PyObject * _PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level); extern PyObject * +_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level); +extern PyObject * _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import); extern PyObject * _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *name, PyObject *builtins, PyObject *globals, diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index afa7bdef4eb..f57b8d2a58c 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -2622,6 +2622,14 @@ def test_compatibility_mode(self): self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules) + def test_compatibility_mode_used(self): + try: + import test.test_import.data.lazy_imports.basic_compatibility_mode_used + except ImportError as e: + self.fail('lazy import failed') + + self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules) + def test_compatibility_mode_relative(self): try: import test.test_import.data.lazy_imports.basic_compatibility_mode_relative diff --git a/Python/ceval.c b/Python/ceval.c index 0a2aa88acde..3918941aed8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2986,10 +2986,81 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi) return 1; } +int +check_lazy_import_comatibility(PyThreadState *tstate, PyObject *lazy_modules, + PyObject *builtins, PyObject *globals, PyObject *locals, + PyObject *name, PyObject *fromlist, PyObject *level, + PyObject **mod) +{ + int ilevel = PyLong_AsInt(level); + if (ilevel == -1 && _PyErr_Occurred(tstate)) { + return -1; + } + + PyObject *abs_name = _PyImport_GetAbsName(tstate, name, globals, ilevel); + if (abs_name == NULL) { + return -1; + } + + int contains = PySequence_Contains(lazy_modules, abs_name); + Py_DECREF(abs_name); + if (contains < 0) { + return -1; + } else if (contains == 0) { + *mod = NULL; + return 0; + } + + _PyInterpreterFrame *frame = _PyEval_GetFrame(); + if (frame == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no current frame"); + return -1; + } + + PyObject *import_func; + if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) { + return -1; + } + + if (import_func == NULL) { + _PyErr_SetString(tstate, PyExc_ImportError, "__import__ not found"); + return -1; + } + + PyObject *final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, import_func, globals, + locals, fromlist, ilevel); + Py_DECREF(import_func); + if (final_mod == NULL) { + return -1; + } + *mod = final_mod; + return 0; +} + PyObject * _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level) { + // Check if this module should be imported lazily due to the compatbility mode support via + // __lazy_modules__. + PyObject *lazy_modules; + if (globals != NULL && + PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) { + return NULL; + } + + PyObject *res; + if (lazy_modules != NULL) { + int lazy = check_lazy_import_comatibility(tstate, lazy_modules, builtins, globals, + locals, name, fromlist, level, &res); + Py_DECREF(lazy_modules); + if (lazy < 0) { + return NULL; + } else if (res != NULL) { + return res; + } + } + PyObject *import_func; if (PyMapping_GetOptionalItem(builtins, &_Py_ID(__import__), &import_func) < 0) { return NULL; @@ -2999,7 +3070,7 @@ _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, return NULL; } - PyObject *res = _PyEval_ImportNameWithImport(tstate, import_func, globals, locals, name, fromlist, level); + res = _PyEval_ImportNameWithImport(tstate, import_func, globals, locals, name, fromlist, level); Py_DECREF(import_func); return res; } diff --git a/Python/import.c b/Python/import.c index 03442d82cf2..cf526acb345 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3956,6 +3956,13 @@ get_abs_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level return Py_NewRef(name); } +PyObject * +_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level) +{ + return get_abs_name(tstate, name, globals, level); +} + + PyObject * PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, @@ -3975,11 +3982,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, goto error; } - if (globals != NULL && - PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) { - goto error; - } - /* The below code is importlib.__import__() & _gcd_import(), ported to C for added performance. */ @@ -3998,44 +4000,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, goto error; } - if (lazy_modules != NULL) { - // Check and see if the module is opting in w/o syntax for backwards compatibility - // with older Python versions. - int contains = PySequence_Contains(lazy_modules, abs_name); - if (contains < 0) { - goto error; - } else if (contains == 1) { - // Don't create lazy import if we're already resolving a lazy import - if (interp->imports.lazy_importing_modules != NULL && - PySet_GET_SIZE(interp->imports.lazy_importing_modules) > 0) { - contains = 0; // Skip lazy import creation - } - } - - if (contains == 1) { - _PyInterpreterFrame *frame = _PyEval_GetFrame(); - if (frame == NULL) { - PyErr_SetString(PyExc_RuntimeError, "no current frame"); - goto error; - } - - PyObject *import_func; - if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) { - return NULL; - } - - if (import_func == NULL) { - _PyErr_SetString(tstate, PyExc_ImportError, "__import__ not found"); - return NULL; - } - - final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, import_func, globals, - locals, fromlist, level); - Py_DECREF(import_func); - goto error; - } - } - mod = import_get_module(tstate, abs_name); if (mod == NULL && _PyErr_Occurred(tstate)) { goto error; @@ -4219,7 +4183,6 @@ register_lazy_on_parent(PyThreadState *tstate, PyObject *name, PyObject *import_ goto done; } if (PyDict_CheckExact(parent_dict) && !PyDict_Contains(parent_dict, child)) { - printf("!!! Adding lazy onto %s %s\n", PyUnicode_AsUTF8(parent), PyUnicode_AsUTF8(child)); PyObject *lazy_module_attr = _PyLazyImport_New(import_func, parent, child); if (lazy_module_attr == NULL) { goto done; @@ -5344,7 +5307,6 @@ _imp__set_lazy_attributes_impl(PyObject *module, PyObject *child_module, Py_hash_t hash; while (_PySet_NextEntry(lazy_submodules, &pos, &attr_name, &hash)) { if (PyDict_Contains(child_dict, attr_name)) { - printf("!!!!!!!! Not replacing %s\n", PyUnicode_AsUTF8(attr_name)); continue; } PyObject *builtins = _PyEval_GetBuiltins(tstate);