Add compatiblity mode

This commit is contained in:
Dino Viehland 2025-09-22 06:43:55 -07:00
parent de281fd894
commit 41ab092407
8 changed files with 53 additions and 7 deletions

View file

@ -1433,6 +1433,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__itruediv__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__itruediv__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ixor__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ixor__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lazy_import__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lazy_import__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lazy_modules__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__le__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__le__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__len__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__len__));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__length_hint__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__length_hint__));
@ -1851,7 +1852,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_type)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_type));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_value)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(last_value));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(latin1)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(latin1));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(lazy));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(leaf_size)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(leaf_size));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(legacy)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(legacy));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(len)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(len));

View file

@ -156,6 +156,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(__itruediv__) STRUCT_FOR_ID(__itruediv__)
STRUCT_FOR_ID(__ixor__) STRUCT_FOR_ID(__ixor__)
STRUCT_FOR_ID(__lazy_import__) STRUCT_FOR_ID(__lazy_import__)
STRUCT_FOR_ID(__lazy_modules__)
STRUCT_FOR_ID(__le__) STRUCT_FOR_ID(__le__)
STRUCT_FOR_ID(__len__) STRUCT_FOR_ID(__len__)
STRUCT_FOR_ID(__length_hint__) STRUCT_FOR_ID(__length_hint__)
@ -574,7 +575,6 @@ struct _Py_global_strings {
STRUCT_FOR_ID(last_type) STRUCT_FOR_ID(last_type)
STRUCT_FOR_ID(last_value) STRUCT_FOR_ID(last_value)
STRUCT_FOR_ID(latin1) STRUCT_FOR_ID(latin1)
STRUCT_FOR_ID(lazy)
STRUCT_FOR_ID(leaf_size) STRUCT_FOR_ID(leaf_size)
STRUCT_FOR_ID(legacy) STRUCT_FOR_ID(legacy)
STRUCT_FOR_ID(len) STRUCT_FOR_ID(len)

View file

@ -1431,6 +1431,7 @@ extern "C" {
INIT_ID(__itruediv__), \ INIT_ID(__itruediv__), \
INIT_ID(__ixor__), \ INIT_ID(__ixor__), \
INIT_ID(__lazy_import__), \ INIT_ID(__lazy_import__), \
INIT_ID(__lazy_modules__), \
INIT_ID(__le__), \ INIT_ID(__le__), \
INIT_ID(__len__), \ INIT_ID(__len__), \
INIT_ID(__length_hint__), \ INIT_ID(__length_hint__), \
@ -1849,7 +1850,6 @@ extern "C" {
INIT_ID(last_type), \ INIT_ID(last_type), \
INIT_ID(last_value), \ INIT_ID(last_value), \
INIT_ID(latin1), \ INIT_ID(latin1), \
INIT_ID(lazy), \
INIT_ID(leaf_size), \ INIT_ID(leaf_size), \
INIT_ID(legacy), \ INIT_ID(legacy), \
INIT_ID(len), \ INIT_ID(len), \

View file

@ -412,6 +412,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1); assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(__lazy_modules__);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(__le__); string = &_Py_ID(__le__);
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
@ -2084,10 +2088,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1); assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(lazy);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(leaf_size); string = &_Py_ID(leaf_size);
_PyUnicode_InternStatic(interp, &string); _PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1)); assert(_PyUnicode_CheckConsistency(string, 1));

View file

@ -2614,6 +2614,23 @@ def test_global_filter_from_true(self):
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules) self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
def test_compatibility_mode(self):
try:
import test.test_import.data.lazy_imports.basic_compatibility_mode
except ImportError as e:
self.fail('lazy import failed')
self.assertFalse("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
except ImportError as e:
self.fail('lazy import failed')
self.assertFalse("test.test_import.data.lazy_imports.basic2" in sys.modules)
class TestSinglePhaseSnapshot(ModuleSnapshot): class TestSinglePhaseSnapshot(ModuleSnapshot):
"""A representation of a single-phase init module for testing. """A representation of a single-phase init module for testing.

View file

@ -0,0 +1,2 @@
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
import test.test_import.data.lazy_imports.basic2

View file

@ -0,0 +1,2 @@
__lazy_modules__ = ['test.test_import.data.lazy_imports.basic2']
lazy from .basic2 import f

View file

@ -3862,6 +3862,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
PyObject *final_mod = NULL; PyObject *final_mod = NULL;
PyObject *mod = NULL; PyObject *mod = NULL;
PyObject *package = NULL; PyObject *package = NULL;
PyObject *lazy_modules = NULL;
PyInterpreterState *interp = tstate->interp; PyInterpreterState *interp = tstate->interp;
int has_from; int has_from;
@ -3870,6 +3871,11 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
goto error; 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 /* The below code is importlib.__import__() & _gcd_import(), ported to C
for added performance. */ for added performance. */
@ -3888,6 +3894,24 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
goto error; 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, name);
if (contains < 0) {
goto error;
} else if (contains == 1) {
_PyInterpreterFrame *frame = _PyEval_GetFrame();
if (frame == NULL) {
PyErr_SetString(PyExc_RuntimeError, "no current frame");
goto error;
}
final_mod = _PyImport_LazyImportModuleLevelObject(tstate, name, frame->f_builtins, globals,
locals, fromlist, level);
goto error;
}
}
mod = import_get_module(tstate, abs_name); mod = import_get_module(tstate, abs_name);
if (mod == NULL && _PyErr_Occurred(tstate)) { if (mod == NULL && _PyErr_Occurred(tstate)) {
goto error; goto error;
@ -3980,6 +4004,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
} }
error: error:
Py_XDECREF(lazy_modules);
Py_XDECREF(abs_name); Py_XDECREF(abs_name);
Py_XDECREF(mod); Py_XDECREF(mod);
Py_XDECREF(package); Py_XDECREF(package);