Skip to content

Commit fe993a0

Browse files
Let the caller of update_extensions_cache() decide if m_copy should be populated.
1 parent af3c1d8 commit fe993a0

File tree

1 file changed

+93
-20
lines changed

1 file changed

+93
-20
lines changed

Python/import.c

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,19 +1185,51 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path)
11851185
return 0;
11861186
}
11871187

1188+
#ifndef NDEBUG
1189+
static bool
1190+
is_singlephase(PyModuleDef *def)
1191+
{
1192+
if (def == NULL) {
1193+
/* It must be a module created by reload_singlephase_extension()
1194+
* from m_copy. Ideally we'd do away with this case. */
1195+
return true;
1196+
}
1197+
else if (def->m_slots == NULL) {
1198+
return true;
1199+
}
1200+
else {
1201+
return false;
1202+
}
1203+
}
1204+
#endif
1205+
1206+
1207+
struct singlephase_global_update {
1208+
PyObject *m_dict;
1209+
};
11881210

11891211
static int
11901212
update_global_state_for_extension(PyThreadState *tstate,
1191-
PyObject *mod, PyModuleDef *def,
1192-
PyObject *name, PyObject *path)
1213+
PyObject *path, PyObject *name,
1214+
PyModuleDef *def,
1215+
struct singlephase_global_update *singlephase)
11931216
{
1194-
assert(mod != NULL && PyModule_Check(mod));
1195-
assert(def == _PyModule_GetDef(mod));
1196-
1197-
// bpo-44050: Extensions and def->m_base.m_copy can be updated
1198-
// when the extension module doesn't support sub-interpreters.
1199-
if (def->m_size == -1) {
1200-
if (!is_core_module(tstate->interp, name, path)) {
1217+
/* Copy the module's __dict__, if applicable. */
1218+
if (singlephase == NULL) {
1219+
assert(def->m_base.m_copy == NULL);
1220+
}
1221+
else {
1222+
assert(def->m_base.m_init != NULL
1223+
|| is_core_module(tstate->interp, name, path));
1224+
if (singlephase->m_dict == NULL) {
1225+
assert(def->m_base.m_copy == NULL);
1226+
}
1227+
else {
1228+
assert(PyDict_Check(singlephase->m_dict));
1229+
// gh-88216: Extensions and def->m_base.m_copy can be updated
1230+
// when the extension module doesn't support sub-interpreters.
1231+
assert(def->m_size == -1);
1232+
assert(!is_core_module(tstate->interp, name, path));
12011233
assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0);
12021234
assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0);
12031235
if (def->m_base.m_copy) {
@@ -1206,17 +1238,16 @@ update_global_state_for_extension(PyThreadState *tstate,
12061238
XXX this should really not happen. */
12071239
Py_CLEAR(def->m_base.m_copy);
12081240
}
1209-
PyObject *dict = PyModule_GetDict(mod);
1210-
if (dict == NULL) {
1211-
return -1;
1212-
}
1213-
def->m_base.m_copy = PyDict_Copy(dict);
1241+
def->m_base.m_copy = PyDict_Copy(singlephase->m_dict);
12141242
if (def->m_base.m_copy == NULL) {
1243+
// XXX Ignore this error? Doing so would effectively
1244+
// mark the module as not loadable. */
12151245
return -1;
12161246
}
12171247
}
12181248
}
12191249

1250+
/* Add the module's def to the global cache. */
12201251
// XXX Why special-case the main interpreter?
12211252
if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
12221253
#ifndef NDEBUG
@@ -1258,6 +1289,8 @@ int
12581289
_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name,
12591290
PyObject *filename, PyObject *modules)
12601291
{
1292+
PyThreadState *tstate = _PyThreadState_GET();
1293+
12611294
if (mod == NULL || !PyModule_Check(mod)) {
12621295
PyErr_BadInternalCall();
12631296
return -1;
@@ -1268,15 +1301,28 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name,
12681301
return -1;
12691302
}
12701303

1271-
PyThreadState *tstate = _PyThreadState_GET();
1304+
/* Only single-phase init extension modules can reach here. */
1305+
assert(is_singlephase(def));
1306+
assert(!is_core_module(tstate->interp, name, filename));
1307+
assert(!is_core_module(tstate->interp, name, name));
1308+
1309+
struct singlephase_global_update singlephase = {0};
1310+
// gh-88216: Extensions and def->m_base.m_copy can be updated
1311+
// when the extension module doesn't support sub-interpreters.
1312+
if (def->m_size == -1) {
1313+
singlephase.m_dict = PyModule_GetDict(mod);
1314+
assert(singlephase.m_dict != NULL);
1315+
}
12721316
if (update_global_state_for_extension(
1273-
tstate, mod, def, name, filename) < 0)
1317+
tstate, filename, name, def, &singlephase) < 0)
12741318
{
12751319
return -1;
12761320
}
1321+
12771322
if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) {
12781323
return -1;
12791324
}
1325+
12801326
return 0;
12811327
}
12821328

@@ -1407,11 +1453,25 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name,
14071453
goto finally;
14081454
}
14091455

1456+
/* We only use _PyImport_FixupBuiltin() for the core builtin modules
1457+
* (sys and builtins). These modules are single-phase init with no
1458+
* module state, but we also don't populate def->m_base.m_copy
1459+
* for them. */
1460+
assert(is_core_module(tstate->interp, nameobj, nameobj));
1461+
assert(is_singlephase(def));
1462+
assert(def->m_size == -1);
1463+
assert(def->m_base.m_copy == NULL);
1464+
1465+
struct singlephase_global_update singlephase = {
1466+
/* We don't want def->m_base.m_copy populated. */
1467+
.m_dict=NULL,
1468+
};
14101469
if (update_global_state_for_extension(
1411-
tstate, mod, def, nameobj, nameobj) < 0)
1470+
tstate, nameobj, nameobj, def, &singlephase) < 0)
14121471
{
14131472
goto finally;
14141473
}
1474+
14151475
if (finish_singlephase_extension(tstate, mod, def, nameobj, modules) < 0) {
14161476
goto finally;
14171477
}
@@ -1444,6 +1504,7 @@ is_builtin(PyObject *name)
14441504
static PyObject*
14451505
create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
14461506
{
1507+
PyModuleDef *def = NULL;
14471508
PyObject *mod = import_find_extension(tstate, name, name);
14481509
if (mod || _PyErr_Occurred(tstate)) {
14491510
return mod;
@@ -1473,20 +1534,32 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
14731534
}
14741535

14751536
if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) {
1476-
return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec);
1537+
def = (PyModuleDef*)mod;
1538+
assert(!is_singlephase(def));
1539+
return PyModule_FromDefAndSpec(def, spec);
14771540
}
14781541
else {
14791542
assert(PyModule_Check(mod));
1480-
PyModuleDef *def = PyModule_GetDef(mod);
1543+
def = PyModule_GetDef(mod);
14811544
if (def == NULL) {
14821545
return NULL;
14831546
}
1547+
assert(is_singlephase(def));
14841548

14851549
/* Remember pointer to module init function. */
14861550
def->m_base.m_init = p0;
14871551

1552+
struct singlephase_global_update singlephase = {0};
1553+
// gh-88216: Extensions and def->m_base.m_copy can be updated
1554+
// when the extension module doesn't support sub-interpreters.
1555+
if (def->m_size == -1
1556+
&& !is_core_module(tstate->interp, name, name))
1557+
{
1558+
singlephase.m_dict = PyModule_GetDict(mod);
1559+
assert(singlephase.m_dict != NULL);
1560+
}
14881561
if (update_global_state_for_extension(
1489-
tstate, mod, def, name, name) < 0)
1562+
tstate, name, name, def, &singlephase) < 0)
14901563
{
14911564
return NULL;
14921565
}

0 commit comments

Comments
 (0)