Skip to content

Commit c8c8838

Browse files
committed
Move __lazy_imports__ check into the interpreter
1 parent 39c33df commit c8c8838

File tree

4 files changed

+89
-46
lines changed

4 files changed

+89
-46
lines changed

Include/internal/pycore_import.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ extern int _PyImport_FixupBuiltin(
3434
extern PyObject *
3535
_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
3636
extern PyObject *
37+
_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
38+
extern PyObject *
3739
_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import);
3840
extern PyObject *
3941
_PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *name, PyObject *builtins, PyObject *globals,

Lib/test/test_import/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2622,6 +2622,14 @@ def test_compatibility_mode(self):
26222622

26232623
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
26242624

2625+
def test_compatibility_mode_used(self):
2626+
try:
2627+
import test.test_import.data.lazy_imports.basic_compatibility_mode_used
2628+
except ImportError as e:
2629+
self.fail('lazy import failed')
2630+
2631+
self.assertTrue("test.test_import.data.lazy_imports.basic2" in sys.modules)
2632+
26252633
def test_compatibility_mode_relative(self):
26262634
try:
26272635
import test.test_import.data.lazy_imports.basic_compatibility_mode_relative

Python/ceval.c

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2986,10 +2986,81 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi)
29862986
return 1;
29872987
}
29882988

2989+
int
2990+
check_lazy_import_comatibility(PyThreadState *tstate, PyObject *lazy_modules,
2991+
PyObject *builtins, PyObject *globals, PyObject *locals,
2992+
PyObject *name, PyObject *fromlist, PyObject *level,
2993+
PyObject **mod)
2994+
{
2995+
int ilevel = PyLong_AsInt(level);
2996+
if (ilevel == -1 && _PyErr_Occurred(tstate)) {
2997+
return -1;
2998+
}
2999+
3000+
PyObject *abs_name = _PyImport_GetAbsName(tstate, name, globals, ilevel);
3001+
if (abs_name == NULL) {
3002+
return -1;
3003+
}
3004+
3005+
int contains = PySequence_Contains(lazy_modules, abs_name);
3006+
Py_DECREF(abs_name);
3007+
if (contains < 0) {
3008+
return -1;
3009+
} else if (contains == 0) {
3010+
*mod = NULL;
3011+
return 0;
3012+
}
3013+
3014+
_PyInterpreterFrame *frame = _PyEval_GetFrame();
3015+
if (frame == NULL) {
3016+
PyErr_SetString(PyExc_RuntimeError, "no current frame");
3017+
return -1;
3018+
}
3019+
3020+
PyObject *import_func;
3021+
if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) {
3022+
return -1;
3023+
}
3024+
3025+
if (import_func == NULL) {
3026+
_PyErr_SetString(tstate, PyExc_ImportError, "__import__ not found");
3027+
return -1;
3028+
}
3029+
3030+
PyObject *final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, import_func, globals,
3031+
locals, fromlist, ilevel);
3032+
Py_DECREF(import_func);
3033+
if (final_mod == NULL) {
3034+
return -1;
3035+
}
3036+
*mod = final_mod;
3037+
return 0;
3038+
}
3039+
29893040
PyObject *
29903041
_PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals,
29913042
PyObject *name, PyObject *fromlist, PyObject *level)
29923043
{
3044+
// Check if this module should be imported lazily due to the compatbility mode support via
3045+
// __lazy_modules__.
3046+
PyObject *lazy_modules;
3047+
if (globals != NULL &&
3048+
PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) {
3049+
return NULL;
3050+
}
3051+
3052+
PyObject *res;
3053+
if (lazy_modules != NULL) {
3054+
int lazy = check_lazy_import_comatibility(tstate, lazy_modules, builtins, globals,
3055+
locals, name, fromlist, level, &res);
3056+
Py_DECREF(lazy_modules);
3057+
if (lazy < 0) {
3058+
return NULL;
3059+
} else if (res != NULL) {
3060+
return res;
3061+
}
3062+
}
3063+
29933064
PyObject *import_func;
29943065
if (PyMapping_GetOptionalItem(builtins, &_Py_ID(__import__), &import_func) < 0) {
29953066
return NULL;
@@ -2999,7 +3070,7 @@ _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals,
29993070
return NULL;
30003071
}
30013072

3002-
PyObject *res = _PyEval_ImportNameWithImport(tstate, import_func, globals, locals, name, fromlist, level);
3073+
res = _PyEval_ImportNameWithImport(tstate, import_func, globals, locals, name, fromlist, level);
30033074
Py_DECREF(import_func);
30043075
return res;
30053076
}

Python/import.c

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3956,6 +3956,13 @@ get_abs_name(PyThreadState *tstate, PyObject *name, PyObject *globals, int level
39563956
return Py_NewRef(name);
39573957
}
39583958

3959+
PyObject *
3960+
_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level)
3961+
{
3962+
return get_abs_name(tstate, name, globals, level);
3963+
}
3964+
3965+
39593966
PyObject *
39603967
PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
39613968
PyObject *locals, PyObject *fromlist,
@@ -3975,11 +3982,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
39753982
goto error;
39763983
}
39773984

3978-
if (globals != NULL &&
3979-
PyMapping_GetOptionalItem(globals, &_Py_ID(__lazy_modules__), &lazy_modules) < 0) {
3980-
goto error;
3981-
}
3982-
39833985
/* The below code is importlib.__import__() & _gcd_import(), ported to C
39843986
for added performance. */
39853987

@@ -3998,44 +4000,6 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
39984000
goto error;
39994001
}
40004002

4001-
if (lazy_modules != NULL) {
4002-
// Check and see if the module is opting in w/o syntax for backwards compatibility
4003-
// with older Python versions.
4004-
int contains = PySequence_Contains(lazy_modules, abs_name);
4005-
if (contains < 0) {
4006-
goto error;
4007-
} else if (contains == 1) {
4008-
// Don't create lazy import if we're already resolving a lazy import
4009-
if (interp->imports.lazy_importing_modules != NULL &&
4010-
PySet_GET_SIZE(interp->imports.lazy_importing_modules) > 0) {
4011-
contains = 0; // Skip lazy import creation
4012-
}
4013-
}
4014-
4015-
if (contains == 1) {
4016-
_PyInterpreterFrame *frame = _PyEval_GetFrame();
4017-
if (frame == NULL) {
4018-
PyErr_SetString(PyExc_RuntimeError, "no current frame");
4019-
goto error;
4020-
}
4021-
4022-
PyObject *import_func;
4023-
if (PyMapping_GetOptionalItem(frame->f_builtins, &_Py_ID(__import__), &import_func) < 0) {
4024-
return NULL;
4025-
}
4026-
4027-
if (import_func == NULL) {
4028-
_PyErr_SetString(tstate, PyExc_ImportError, "__import__ not found");
4029-
return NULL;
4030-
}
4031-
4032-
final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, import_func, globals,
4033-
locals, fromlist, level);
4034-
Py_DECREF(import_func);
4035-
goto error;
4036-
}
4037-
}
4038-
40394003
mod = import_get_module(tstate, abs_name);
40404004
if (mod == NULL && _PyErr_Occurred(tstate)) {
40414005
goto error;
@@ -4219,7 +4183,6 @@ register_lazy_on_parent(PyThreadState *tstate, PyObject *name, PyObject *import_
42194183
goto done;
42204184
}
42214185
if (PyDict_CheckExact(parent_dict) && !PyDict_Contains(parent_dict, child)) {
4222-
printf("!!! Adding lazy onto %s %s\n", PyUnicode_AsUTF8(parent), PyUnicode_AsUTF8(child));
42234186
PyObject *lazy_module_attr = _PyLazyImport_New(import_func, parent, child);
42244187
if (lazy_module_attr == NULL) {
42254188
goto done;
@@ -5344,7 +5307,6 @@ _imp__set_lazy_attributes_impl(PyObject *module, PyObject *child_module,
53445307
Py_hash_t hash;
53455308
while (_PySet_NextEntry(lazy_submodules, &pos, &attr_name, &hash)) {
53465309
if (PyDict_Contains(child_dict, attr_name)) {
5347-
printf("!!!!!!!! Not replacing %s\n", PyUnicode_AsUTF8(attr_name));
53485310
continue;
53495311
}
53505312
PyObject *builtins = _PyEval_GetBuiltins(tstate);

0 commit comments

Comments
 (0)