@@ -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
11891211static int
11901212update_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)
14441504static PyObject *
14451505create_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