diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index f57b8d2a58c..4526a6d8953 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -2630,6 +2630,22 @@ def test_compatibility_mode_used(self): self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules) + def test_compatibility_mode_func(self): + try: + import test.test_import.data.lazy_imports.compatibility_mode_func + 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_try_except(self): + try: + import test.test_import.data.lazy_imports.compatibility_mode_try_except + 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/Lib/test/test_import/data/lazy_imports/basic_compatibility_mode_used.py b/Lib/test/test_import/data/lazy_imports/basic_compatibility_mode_used.py new file mode 100644 index 00000000000..64f36645f68 --- /dev/null +++ b/Lib/test/test_import/data/lazy_imports/basic_compatibility_mode_used.py @@ -0,0 +1,3 @@ +__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2'] +import test.test_import.data.lazy_imports.basic2 +test.test_import.data.lazy_imports.basic2.f() diff --git a/Lib/test/test_import/data/lazy_imports/compatibility_mode_func.py b/Lib/test/test_import/data/lazy_imports/compatibility_mode_func.py new file mode 100644 index 00000000000..307338a0886 --- /dev/null +++ b/Lib/test/test_import/data/lazy_imports/compatibility_mode_func.py @@ -0,0 +1,5 @@ +__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2'] +def f(): + import test.test_import.data.lazy_imports.basic2 + +f() diff --git a/Lib/test/test_import/data/lazy_imports/compatibility_mode_try_except.py b/Lib/test/test_import/data/lazy_imports/compatibility_mode_try_except.py new file mode 100644 index 00000000000..6d54e69a9a4 --- /dev/null +++ b/Lib/test/test_import/data/lazy_imports/compatibility_mode_try_except.py @@ -0,0 +1,5 @@ +__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2'] +try: + import test.test_import.data.lazy_imports.basic2 +except: + pass diff --git a/Python/ceval.c b/Python/ceval.c index 3918941aed8..85835d99230 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2986,81 +2986,10 @@ _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; @@ -3070,7 +2999,7 @@ _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, return NULL; } - res = _PyEval_ImportNameWithImport(tstate, import_func, globals, locals, name, fromlist, level); + PyObject *res = _PyEval_ImportNameWithImport(tstate, import_func, globals, locals, name, fromlist, level); Py_DECREF(import_func); return res; } @@ -3102,10 +3031,37 @@ _PyEval_ImportNameWithImport(PyThreadState *tstate, PyObject *import_func, PyObj return res; } +int +check_lazy_import_comatibility(PyThreadState *tstate, PyObject *globals, + PyObject *name, PyObject *level) +{ + // Check if this module should be imported lazily due to the compatbility mode support via + // __lazy_modules__. + PyObject *lazy_modules = NULL; + if (globals != NULL && + PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) { + return -1; + } else if (lazy_modules == NULL) { + return 0; + } + + 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; + } + + return PySequence_Contains(lazy_modules, abs_name); +} PyObject * _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, - PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level, int lazy) + PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level, + int lazy) { PyObject *res = NULL; // Check if global policy overrides the local syntax @@ -3120,6 +3076,14 @@ _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *glob break; } + if (!lazy) { + // see if __lazy_imports__ forces this to be lazy + lazy = check_lazy_import_comatibility(tstate, globals, name, level); + if (lazy < 0) { + return NULL; + } + } + if (!lazy) { // Not a lazy import or lazy imports are disabled, fallback to the regular import return _PyEval_ImportName(tstate, builtins, globals, locals, name, fromlist, level);