Skip to content

Commit ce8c909

Browse files
committed
pythongh-155695: Keep lazy-module state inspection conservative
Use the existing inline-attribute accessor for normal ModuleSpec state. Leave unsupported spec layouts and flag values tracked instead of handling arbitrary dictionaries, descriptors, or truth conversions.
1 parent 5fa018a commit ce8c909

2 files changed

Lines changed: 21 additions & 82 deletions

File tree

‎Lib/test/test_lazy_import/__init__.py‎

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,16 +1467,11 @@ class SlottedSpec:
14671467
def _initializing(self):
14681468
raise RuntimeError("_initializing descriptor was run")
14691469
1470-
class EmptySpec:
1471-
__slots__ = ()
1472-
1473-
for spec in (Spec(), SlottedSpec(), EmptySpec(), object()):
1474-
sys.lazy_modules.discard("custom_spec")
1470+
for spec in (Spec(), SlottedSpec()):
14751471
module = types.ModuleType("custom_spec")
14761472
module.__spec__ = spec
14771473
sys.modules["custom_spec"] = module
14781474
lazy import custom_spec
1479-
assert "custom_spec" in sys.lazy_modules, sys.lazy_modules
14801475
""")
14811476
assert_python_ok("-c", code)
14821477

@@ -1505,35 +1500,26 @@ def _initializing(self):
15051500
""")
15061501
assert_python_ok("-c", code)
15071502

1508-
def test_spec_builtin_false_initializing_is_not_tracked(self):
1509-
code = textwrap.dedent("""
1510-
import sys
1511-
import types
1512-
1513-
for flag in (False, None, 0, 0.0, 0j, "", b"", bytearray(),
1514-
(), [], {}, set(), frozenset(), range(0), memoryview(b"")):
1515-
module = types.ModuleType("custom_spec")
1516-
module.__spec__ = types.SimpleNamespace(_initializing=flag)
1517-
sys.modules["custom_spec"] = module
1518-
lazy import custom_spec
1519-
assert "custom_spec" not in sys.lazy_modules, (flag, sys.lazy_modules)
1520-
""")
1521-
assert_python_ok("-c", code)
1522-
15231503
def test_spec_initializing_truth_conversion_is_not_run(self):
15241504
code = textwrap.dedent("""
15251505
import sys
15261506
import types
1507+
from importlib.machinery import ModuleSpec
15271508
1528-
class Flag(int):
1509+
class Flag:
15291510
def __bool__(self):
15301511
raise RuntimeError("_initializing truth conversion was run")
15311512
1532-
module = types.ModuleType("custom_spec")
1533-
module.__spec__ = types.SimpleNamespace(_initializing=Flag())
1534-
sys.modules["custom_spec"] = module
1535-
lazy import custom_spec
1536-
assert "custom_spec" in sys.lazy_modules, sys.lazy_modules
1513+
released = memoryview(b"")
1514+
released.release()
1515+
for flag in (Flag(), released):
1516+
sys.lazy_modules.discard("custom_spec")
1517+
module = types.ModuleType("custom_spec")
1518+
module.__spec__ = ModuleSpec("custom_spec", None)
1519+
module.__spec__._initializing = flag
1520+
sys.modules["custom_spec"] = module
1521+
lazy import custom_spec
1522+
assert "custom_spec" in sys.lazy_modules, sys.lazy_modules
15371523
""")
15381524
assert_python_ok("-c", code)
15391525

‎Python/import.c‎

Lines changed: 8 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4376,69 +4376,22 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
43764376
return final_mod;
43774377
}
43784378

4379-
// Inspect stored initialization state without running spec descriptors or
4380-
// converting user-defined values to bool. Unknown state stays pending.
4379+
// Inspect the usual ModuleSpec flag without running callbacks or creating a
4380+
// dictionary. Keep unsupported spec representations conservatively tracked.
43814381
static int
43824382
lazy_import_spec_is_initializing(PyObject *spec)
43834383
{
43844384
if (spec == Py_None) {
43854385
return 0;
43864386
}
4387-
PyObject *initializing = NULL;
4388-
int rc = 0;
4389-
if ((Py_TYPE(spec)->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
4390-
_PyObject_TryGetInstanceAttribute(spec, &_Py_ID(_initializing), &initializing)) {
4391-
rc = initializing != NULL;
4392-
}
4393-
else {
4394-
// Read the existing dictionary without materializing inline values.
4395-
PyObject *dict = NULL;
4396-
bool has_dict = true;
4397-
Py_BEGIN_CRITICAL_SECTION(spec);
4398-
if (Py_TYPE(spec)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
4399-
dict = Py_XNewRef((PyObject *)_PyObject_GetManagedDict(spec));
4400-
}
4401-
else {
4402-
PyObject **dictptr = _PyObject_ComputedDictPointer(spec);
4403-
if (dictptr != NULL) {
4404-
dict = Py_XNewRef(*dictptr);
4405-
}
4406-
else {
4407-
has_dict = false;
4408-
}
4409-
}
4410-
Py_END_CRITICAL_SECTION();
4411-
if (!has_dict) {
4412-
return 1;
4413-
}
4414-
if (dict != NULL) {
4415-
rc = PyDict_GetItemRef(dict, &_Py_ID(_initializing), &initializing);
4416-
Py_DECREF(dict);
4417-
if (rc < 0) {
4418-
return -1;
4419-
}
4420-
}
4421-
}
4422-
if (rc == 0) {
4423-
initializing = _PyType_LookupRef(Py_TYPE(spec), &_Py_ID(_initializing));
4424-
}
4425-
if (initializing == NULL || initializing == Py_None) {
4426-
rc = 0;
4427-
}
4428-
else if (PyBool_Check(initializing) || PyLong_CheckExact(initializing) ||
4429-
PyFloat_CheckExact(initializing) || PyComplex_CheckExact(initializing) ||
4430-
PyUnicode_CheckExact(initializing) || PyBytes_CheckExact(initializing) ||
4431-
PyByteArray_CheckExact(initializing) || PyTuple_CheckExact(initializing) ||
4432-
PyList_CheckExact(initializing) || PyDict_CheckExact(initializing) ||
4433-
PyAnySet_CheckExact(initializing) || PyMemoryView_Check(initializing) ||
4434-
Py_IS_TYPE(initializing, &PyRange_Type)) {
4435-
rc = PyObject_IsTrue(initializing);
4436-
}
4437-
else {
4438-
rc = 1;
4387+
PyObject *initializing;
4388+
if (!(Py_TYPE(spec)->tp_flags & Py_TPFLAGS_INLINE_VALUES) ||
4389+
!_PyObject_TryGetInstanceAttribute(spec, &_Py_ID(_initializing), &initializing)) {
4390+
return 1;
44394391
}
4392+
int res = initializing != NULL && initializing != Py_False;
44404393
Py_XDECREF(initializing);
4441-
return rc;
4394+
return res;
44424395
}
44434396

44444397
// Check if a module is already loaded before adding it to sys.lazy_modules

0 commit comments

Comments
 (0)