From d87d2128422ab3a37563d6d6501a395724fb53f5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 22 Apr 2024 15:31:10 -0600 Subject: [PATCH 01/11] Factor out fix_up_extension_for_interpreter(). --- Include/internal/pycore_import.h | 1 + Python/import.c | 100 ++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 08af53258cde97..3bde64daa87e62 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -28,6 +28,7 @@ extern int _PyImport_FixupBuiltin( const char *name, /* UTF-8 encoded string */ PyObject *modules ); +// We could probably drop this: extern int _PyImport_FixupExtensionObject(PyObject*, PyObject *, PyObject *, PyObject *); diff --git a/Python/import.c b/Python/import.c index 8cdc04f03dd201..c1076501a133ed 100644 --- a/Python/import.c +++ b/Python/import.c @@ -200,16 +200,22 @@ _PyImport_ClearModules(PyInterpreterState *interp) Py_SETREF(MODULES(interp), NULL); } -PyObject * -PyImport_GetModuleDict(void) +static inline PyObject * +get_modules_dict(PyInterpreterState *interp) { - PyInterpreterState *interp = _PyInterpreterState_GET(); if (MODULES(interp) == NULL) { Py_FatalError("interpreter has no modules dictionary"); } return MODULES(interp); } +PyObject * +PyImport_GetModuleDict(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + return get_modules_dict(interp); +} + int _PyImport_SetModule(PyObject *name, PyObject *m) { @@ -894,7 +900,7 @@ extensions_lock_release(void) (module name, module name) (for built-in modules) or by (filename, module name) (for dynamically loaded modules), containing these modules. A copy of the module's dictionary is stored by calling - _PyImport_FixupExtensionObject() immediately after the module initialization + fix_up_extension() immediately after the module initialization function succeeds. A copy can be retrieved from there by calling import_find_extension(). @@ -1159,28 +1165,57 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) } static int -fix_up_extension(PyObject *mod, PyObject *name, PyObject *path) +fix_up_extension_for_interpreter(PyInterpreterState *interp, + PyObject *mod, PyModuleDef *def, + PyObject *name, PyObject *path, + PyObject *modules) { - if (mod == NULL || !PyModule_Check(mod)) { - PyErr_BadInternalCall(); + assert(mod != NULL && PyModule_Check(mod)); + assert(def == PyModule_GetDef(mod)); + + if (modules == NULL) { + modules = get_modules_dict(interp); + } + if (PyObject_SetItem(modules, name, mod) < 0) { return -1; } - struct PyModuleDef *def = PyModule_GetDef(mod); - if (!def) { - PyErr_BadInternalCall(); - return -1; + if (_modules_by_index_set(interp, def, mod) < 0) { + goto error; } - PyThreadState *tstate = _PyThreadState_GET(); - if (_modules_by_index_set(tstate->interp, def, mod) < 0) { + return 0; + +error: + PyMapping_DelItem(modules, name); + return -1; +} + + +static int +fix_up_extension(PyObject *mod, PyModuleDef *def, + PyObject *name, PyObject *path, + PyObject *modules) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (def == NULL) { + def = PyModule_GetDef(mod); + if (def == NULL) { + PyErr_BadInternalCall(); + return -1; + } + } + + if (fix_up_extension_for_interpreter( + interp, mod, def, name, path, modules) < 0) + { return -1; } // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. if (def->m_size == -1) { - if (!is_core_module(tstate->interp, name, path)) { + if (!is_core_module(interp, name, path)) { assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0); assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0); if (def->m_base.m_copy) { @@ -1191,34 +1226,43 @@ fix_up_extension(PyObject *mod, PyObject *name, PyObject *path) } PyObject *dict = PyModule_GetDict(mod); if (dict == NULL) { - return -1; + goto error; } def->m_base.m_copy = PyDict_Copy(dict); if (def->m_base.m_copy == NULL) { - return -1; + goto error; } } } // XXX Why special-case the main interpreter? - if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { + if (_Py_IsMainInterpreter(interp) || def->m_size == -1) { +#ifndef NDEBUG + PyModuleDef *cached = _extensions_cache_get(path, name); + assert(cached == NULL || cached == def); +#endif if (_extensions_cache_set(path, name, def) < 0) { - return -1; + goto error; } } return 0; + + +error: + PyMapping_DelItem(modules, name); + return -1; } int _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyObject *filename, PyObject *modules) { - if (PyObject_SetItem(modules, name, mod) < 0) { + if (mod == NULL || !PyModule_Check(mod)) { + PyErr_BadInternalCall(); return -1; } - if (fix_up_extension(mod, name, filename) < 0) { - PyMapping_DelItem(modules, name); + if (fix_up_extension(mod, NULL, name, filename, modules) < 0) { return -1; } return 0; @@ -1350,11 +1394,7 @@ _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) goto finally; } - if (PyObject_SetItem(modules, nameobj, mod) < 0) { - goto finally; - } - if (fix_up_extension(mod, nameobj, nameobj) < 0) { - PyMapping_DelItem(modules, nameobj); + if (fix_up_extension(mod, def, nameobj, nameobj, modules) < 0) { goto finally; } @@ -1391,7 +1431,6 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return mod; } - PyObject *modules = MODULES(tstate->interp); struct _inittab *found = NULL; for (struct _inittab *p = INITTAB; p->name != NULL; p++) { if (_PyUnicode_EqualToASCIIString(name, p->name)) { @@ -1419,14 +1458,17 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec); } else { - /* Remember pointer to module init function. */ + assert(PyModule_Check(mod)); PyModuleDef *def = PyModule_GetDef(mod); if (def == NULL) { return NULL; } + /* Remember pointer to module init function. */ def->m_base.m_init = p0; - if (_PyImport_FixupExtensionObject(mod, name, name, modules) < 0) { + + PyObject *modules = MODULES(tstate->interp); + if (fix_up_extension(mod, def, name, name, modules) < 0) { return NULL; } return mod; From b85c5afb269197d4724be688050b99605ce9e985 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 17 Apr 2024 11:31:31 -0600 Subject: [PATCH 02/11] Pass tstate through to fix_up_extension(). --- Include/internal/pycore_import.h | 1 + Python/import.c | 25 +++++++++++++------------ Python/pylifecycle.c | 2 +- Python/sysmodule.c | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_import.h b/Include/internal/pycore_import.h index 3bde64daa87e62..8d7f0543f8d315 100644 --- a/Include/internal/pycore_import.h +++ b/Include/internal/pycore_import.h @@ -24,6 +24,7 @@ extern int _PyImport_ReleaseLock(PyInterpreterState *interp); // This is used exclusively for the sys and builtins modules: extern int _PyImport_FixupBuiltin( + PyThreadState *tstate, PyObject *mod, const char *name, /* UTF-8 encoded string */ PyObject *modules diff --git a/Python/import.c b/Python/import.c index c1076501a133ed..8779350d4d196c 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1165,7 +1165,7 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) } static int -fix_up_extension_for_interpreter(PyInterpreterState *interp, +fix_up_extension_for_interpreter(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, PyObject *name, PyObject *path, PyObject *modules) @@ -1174,13 +1174,13 @@ fix_up_extension_for_interpreter(PyInterpreterState *interp, assert(def == PyModule_GetDef(mod)); if (modules == NULL) { - modules = get_modules_dict(interp); + modules = get_modules_dict(tstate->interp); } if (PyObject_SetItem(modules, name, mod) < 0) { return -1; } - if (_modules_by_index_set(interp, def, mod) < 0) { + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { goto error; } @@ -1193,11 +1193,10 @@ fix_up_extension_for_interpreter(PyInterpreterState *interp, static int -fix_up_extension(PyObject *mod, PyModuleDef *def, +fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, PyObject *name, PyObject *path, PyObject *modules) { - PyInterpreterState *interp = _PyInterpreterState_GET(); if (def == NULL) { def = PyModule_GetDef(mod); if (def == NULL) { @@ -1207,7 +1206,7 @@ fix_up_extension(PyObject *mod, PyModuleDef *def, } if (fix_up_extension_for_interpreter( - interp, mod, def, name, path, modules) < 0) + tstate, mod, def, name, path, modules) < 0) { return -1; } @@ -1215,7 +1214,7 @@ fix_up_extension(PyObject *mod, PyModuleDef *def, // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. if (def->m_size == -1) { - if (!is_core_module(interp, name, path)) { + if (!is_core_module(tstate->interp, name, path)) { assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0); assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0); if (def->m_base.m_copy) { @@ -1236,7 +1235,7 @@ fix_up_extension(PyObject *mod, PyModuleDef *def, } // XXX Why special-case the main interpreter? - if (_Py_IsMainInterpreter(interp) || def->m_size == -1) { + if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) { #ifndef NDEBUG PyModuleDef *cached = _extensions_cache_get(path, name); assert(cached == NULL || cached == def); @@ -1262,7 +1261,8 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyErr_BadInternalCall(); return -1; } - if (fix_up_extension(mod, NULL, name, filename, modules) < 0) { + PyThreadState *tstate = _PyThreadState_GET(); + if (fix_up_extension(tstate, mod, NULL, name, filename, modules) < 0) { return -1; } return 0; @@ -1377,7 +1377,8 @@ clear_singlephase_extension(PyInterpreterState *interp, /*******************/ int -_PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) +_PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, + PyObject *modules) { int res = -1; assert(mod != NULL && PyModule_Check(mod)); @@ -1394,7 +1395,7 @@ _PyImport_FixupBuiltin(PyObject *mod, const char *name, PyObject *modules) goto finally; } - if (fix_up_extension(mod, def, nameobj, nameobj, modules) < 0) { + if (fix_up_extension(tstate, mod, def, nameobj, nameobj, modules) < 0) { goto finally; } @@ -1468,7 +1469,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) def->m_base.m_init = p0; PyObject *modules = MODULES(tstate->interp); - if (fix_up_extension(mod, def, name, name, modules) < 0) { + if (fix_up_extension(tstate, mod, def, name, name, modules) < 0) { return NULL; } return mod; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index cc1824634e7a7f..0f3ca4a6687514 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -777,7 +777,7 @@ pycore_init_builtins(PyThreadState *tstate) } PyObject *modules = _PyImport_GetModules(interp); - if (_PyImport_FixupBuiltin(bimod, "builtins", modules) < 0) { + if (_PyImport_FixupBuiltin(tstate, bimod, "builtins", modules) < 0) { goto error; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 05ee4051a20e18..7af363678e8e86 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3764,7 +3764,7 @@ _PySys_Create(PyThreadState *tstate, PyObject **sysmod_p) return status; } - if (_PyImport_FixupBuiltin(sysmod, "sys", modules) < 0) { + if (_PyImport_FixupBuiltin(tstate, sysmod, "sys", modules) < 0) { goto error; } From 319c4613182d9b42c447b2255dd699a63900dd0c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 18 Apr 2024 19:16:44 -0600 Subject: [PATCH 03/11] We already know the def is okay. --- Python/import.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Python/import.c b/Python/import.c index 8779350d4d196c..ef32720146b00b 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1197,13 +1197,8 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, PyObject *name, PyObject *path, PyObject *modules) { - if (def == NULL) { - def = PyModule_GetDef(mod); - if (def == NULL) { - PyErr_BadInternalCall(); - return -1; - } - } + assert(mod != NULL && PyModule_Check(mod)); + assert(def == _PyModule_GetDef(mod)); if (fix_up_extension_for_interpreter( tstate, mod, def, name, path, modules) < 0) @@ -1247,7 +1242,6 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, return 0; - error: PyMapping_DelItem(modules, name); return -1; @@ -1261,8 +1255,14 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyErr_BadInternalCall(); return -1; } + PyModuleDef *def = PyModule_GetDef(mod); + if (def == NULL) { + PyErr_BadInternalCall(); + return -1; + } + PyThreadState *tstate = _PyThreadState_GET(); - if (fix_up_extension(tstate, mod, NULL, name, filename, modules) < 0) { + if (fix_up_extension(tstate, mod, def, name, filename, modules) < 0) { return -1; } return 0; From e6ea054525ee34af451c07eb8b7dcf2738d7ce85 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 19 Apr 2024 14:36:40 -0600 Subject: [PATCH 04/11] Let the modules arg to fix_up_extension_for_interpreter() be NULL. --- Python/import.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Python/import.c b/Python/import.c index ef32720146b00b..3685e622e798a3 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1167,28 +1167,22 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) static int fix_up_extension_for_interpreter(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, - PyObject *name, PyObject *path, - PyObject *modules) + PyObject *name, PyObject *modules) { assert(mod != NULL && PyModule_Check(mod)); assert(def == PyModule_GetDef(mod)); - if (modules == NULL) { - modules = get_modules_dict(tstate->interp); - } - if (PyObject_SetItem(modules, name, mod) < 0) { + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { return -1; } - if (_modules_by_index_set(tstate->interp, def, mod) < 0) { - goto error; + if (modules != NULL) { + if (PyObject_SetItem(modules, name, mod) < 0) { + return -1; + } } return 0; - -error: - PyMapping_DelItem(modules, name); - return -1; } @@ -1201,7 +1195,7 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, assert(def == _PyModule_GetDef(mod)); if (fix_up_extension_for_interpreter( - tstate, mod, def, name, path, modules) < 0) + tstate, mod, def, name, modules) < 0) { return -1; } From 439661f496715e944c217f4611c13e4aee395a47 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 17 Apr 2024 12:43:19 -0600 Subject: [PATCH 05/11] Always expect a valid modules dict passed to fix_up_extension(). --- Python/import.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/Python/import.c b/Python/import.c index 3685e622e798a3..d0c81bc9089550 100644 --- a/Python/import.c +++ b/Python/import.c @@ -201,44 +201,53 @@ _PyImport_ClearModules(PyInterpreterState *interp) } static inline PyObject * -get_modules_dict(PyInterpreterState *interp) +get_modules_dict(PyThreadState *tstate, bool fatal) { - if (MODULES(interp) == NULL) { - Py_FatalError("interpreter has no modules dictionary"); + /* Technically, it would make sense to incref the dict, + * since sys.modules could be swapped out and decref'ed to 0 + * before the caller is done using it. However, that is highly + * unlikely, especially since we can rely on a global lock + * (i.e. the GIL) for thread-safety. */ + PyObject *modules = MODULES(tstate->interp); + if (modules == NULL) { + if (fatal) { + Py_FatalError("interpreter has no modules dictionary"); + } + _PyErr_SetString(tstate, PyExc_RuntimeError, + "unable to get sys.modules"); + return NULL; } - return MODULES(interp); + return modules; } PyObject * PyImport_GetModuleDict(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return get_modules_dict(interp); + PyThreadState *tstate = _PyThreadState_GET(); + return get_modules_dict(tstate, true); } int _PyImport_SetModule(PyObject *name, PyObject *m) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = MODULES(interp); + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *modules = get_modules_dict(tstate, true); return PyObject_SetItem(modules, name, m); } int _PyImport_SetModuleString(const char *name, PyObject *m) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyObject *modules = MODULES(interp); + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *modules = get_modules_dict(tstate, true); return PyMapping_SetItemString(modules, name, m); } static PyObject * import_get_module(PyThreadState *tstate, PyObject *name) { - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, false); if (modules == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "unable to get sys.modules"); return NULL; } @@ -303,10 +312,8 @@ PyImport_GetModule(PyObject *name) static PyObject * import_add_module(PyThreadState *tstate, PyObject *name) { - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, false); if (modules == NULL) { - _PyErr_SetString(tstate, PyExc_RuntimeError, - "no import module dictionary"); return NULL; } @@ -403,7 +410,7 @@ remove_module(PyThreadState *tstate, PyObject *name) { PyObject *exc = _PyErr_GetRaisedException(tstate); - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, true); if (PyDict_CheckExact(modules)) { // Error is reported to the caller (void)PyDict_Pop(modules, name, NULL); @@ -1283,7 +1290,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name, } PyObject *mod, *mdict; - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, true); if (def->m_size == -1) { PyObject *m_copy = def->m_base.m_copy; @@ -1462,7 +1469,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) /* Remember pointer to module init function. */ def->m_base.m_init = p0; - PyObject *modules = MODULES(tstate->interp); + PyObject *modules = get_modules_dict(tstate, true); if (fix_up_extension(tstate, mod, def, name, name, modules) < 0) { return NULL; } From 0d28e0faa47c80797fd3323d6c11f2590ec785eb Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 08:38:54 -0600 Subject: [PATCH 06/11] Do interpreter fixups after the extension cache, not before. --- Python/import.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Python/import.c b/Python/import.c index d0c81bc9089550..5cf55dd2a74a2a 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1192,7 +1192,6 @@ fix_up_extension_for_interpreter(PyThreadState *tstate, return 0; } - static int fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, PyObject *name, PyObject *path, @@ -1201,12 +1200,6 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, assert(mod != NULL && PyModule_Check(mod)); assert(def == _PyModule_GetDef(mod)); - if (fix_up_extension_for_interpreter( - tstate, mod, def, name, modules) < 0) - { - return -1; - } - // bpo-44050: Extensions and def->m_base.m_copy can be updated // when the extension module doesn't support sub-interpreters. if (def->m_size == -1) { @@ -1221,11 +1214,11 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, } PyObject *dict = PyModule_GetDict(mod); if (dict == NULL) { - goto error; + return -1; } def->m_base.m_copy = PyDict_Copy(dict); if (def->m_base.m_copy == NULL) { - goto error; + return -1; } } } @@ -1237,15 +1230,17 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, assert(cached == NULL || cached == def); #endif if (_extensions_cache_set(path, name, def) < 0) { - goto error; + return -1; } } - return 0; + if (fix_up_extension_for_interpreter( + tstate, mod, def, name, modules) < 0) + { + return -1; + } -error: - PyMapping_DelItem(modules, name); - return -1; + return 0; } int From abd69f44136d345328fe79190dd0e8557ef6d51a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 08:49:25 -0600 Subject: [PATCH 07/11] Call fix_up_extension_for_interpreter() separately. --- Python/import.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Python/import.c b/Python/import.c index 5cf55dd2a74a2a..cfc70498d1cfad 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1194,8 +1194,7 @@ fix_up_extension_for_interpreter(PyThreadState *tstate, static int fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, - PyObject *name, PyObject *path, - PyObject *modules) + PyObject *name, PyObject *path) { assert(mod != NULL && PyModule_Check(mod)); assert(def == _PyModule_GetDef(mod)); @@ -1234,12 +1233,6 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, } } - if (fix_up_extension_for_interpreter( - tstate, mod, def, name, modules) < 0) - { - return -1; - } - return 0; } @@ -1258,7 +1251,12 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, } PyThreadState *tstate = _PyThreadState_GET(); - if (fix_up_extension(tstate, mod, def, name, filename, modules) < 0) { + if (fix_up_extension(tstate, mod, def, name, filename) < 0) { + return -1; + } + if (fix_up_extension_for_interpreter( + tstate, mod, def, name, modules) < 0) + { return -1; } return 0; @@ -1391,7 +1389,12 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, goto finally; } - if (fix_up_extension(tstate, mod, def, nameobj, nameobj, modules) < 0) { + if (fix_up_extension(tstate, mod, def, nameobj, nameobj) < 0) { + goto finally; + } + if (fix_up_extension_for_interpreter( + tstate, mod, def, nameobj, modules) < 0) + { goto finally; } @@ -1464,8 +1467,13 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) /* Remember pointer to module init function. */ def->m_base.m_init = p0; + if (fix_up_extension(tstate, mod, def, name, name) < 0) { + return NULL; + } PyObject *modules = get_modules_dict(tstate, true); - if (fix_up_extension(tstate, mod, def, name, name, modules) < 0) { + if (fix_up_extension_for_interpreter( + tstate, mod, def, name, modules) < 0) + { return NULL; } return mod; From f347f8d0fb0c6857f73d87fda573e110ebf5c589 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 08:54:07 -0600 Subject: [PATCH 08/11] fix_up_extension_for_interpreter() -> finish_singlephase_extension() --- Python/import.c | 55 +++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/Python/import.c b/Python/import.c index cfc70498d1cfad..e762870b7344b4 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1171,26 +1171,6 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) return 0; } -static int -fix_up_extension_for_interpreter(PyThreadState *tstate, - PyObject *mod, PyModuleDef *def, - PyObject *name, PyObject *modules) -{ - assert(mod != NULL && PyModule_Check(mod)); - assert(def == PyModule_GetDef(mod)); - - if (_modules_by_index_set(tstate->interp, def, mod) < 0) { - return -1; - } - - if (modules != NULL) { - if (PyObject_SetItem(modules, name, mod) < 0) { - return -1; - } - } - - return 0; -} static int fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, @@ -1236,6 +1216,29 @@ fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, return 0; } +/* For multi-phase init modules, the module is finished + * by PyModule_FromDefAndSpec(). */ +static int +finish_singlephase_extension(PyThreadState *tstate, + PyObject *mod, PyModuleDef *def, + PyObject *name, PyObject *modules) +{ + assert(mod != NULL && PyModule_Check(mod)); + assert(def == PyModule_GetDef(mod)); + + if (_modules_by_index_set(tstate->interp, def, mod) < 0) { + return -1; + } + + if (modules != NULL) { + if (PyObject_SetItem(modules, name, mod) < 0) { + return -1; + } + } + + return 0; +} + int _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, PyObject *filename, PyObject *modules) @@ -1254,9 +1257,7 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, if (fix_up_extension(tstate, mod, def, name, filename) < 0) { return -1; } - if (fix_up_extension_for_interpreter( - tstate, mod, def, name, modules) < 0) - { + if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { return -1; } return 0; @@ -1392,9 +1393,7 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, if (fix_up_extension(tstate, mod, def, nameobj, nameobj) < 0) { goto finally; } - if (fix_up_extension_for_interpreter( - tstate, mod, def, nameobj, modules) < 0) - { + if (finish_singlephase_extension(tstate, mod, def, nameobj, modules) < 0) { goto finally; } @@ -1471,9 +1470,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) return NULL; } PyObject *modules = get_modules_dict(tstate, true); - if (fix_up_extension_for_interpreter( - tstate, mod, def, name, modules) < 0) - { + if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { return NULL; } return mod; From 49760890beec60dff8b07485d36ca33bfb982387 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 09:40:52 -0600 Subject: [PATCH 09/11] fix_up_extension() -> update_global_state_for_extension() --- Python/import.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Python/import.c b/Python/import.c index e762870b7344b4..5331657a9bf34d 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1173,8 +1173,9 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path) static int -fix_up_extension(PyThreadState *tstate, PyObject *mod, PyModuleDef *def, - PyObject *name, PyObject *path) +update_global_state_for_extension(PyThreadState *tstate, + PyObject *mod, PyModuleDef *def, + PyObject *name, PyObject *path) { assert(mod != NULL && PyModule_Check(mod)); assert(def == _PyModule_GetDef(mod)); @@ -1254,7 +1255,9 @@ _PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, } PyThreadState *tstate = _PyThreadState_GET(); - if (fix_up_extension(tstate, mod, def, name, filename) < 0) { + if (update_global_state_for_extension( + tstate, mod, def, name, filename) < 0) + { return -1; } if (finish_singlephase_extension(tstate, mod, def, name, modules) < 0) { @@ -1390,7 +1393,9 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name, goto finally; } - if (fix_up_extension(tstate, mod, def, nameobj, nameobj) < 0) { + if (update_global_state_for_extension( + tstate, mod, def, nameobj, nameobj) < 0) + { goto finally; } if (finish_singlephase_extension(tstate, mod, def, nameobj, modules) < 0) { @@ -1466,7 +1471,9 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec) /* Remember pointer to module init function. */ def->m_base.m_init = p0; - if (fix_up_extension(tstate, mod, def, name, name) < 0) { + if (update_global_state_for_extension( + tstate, mod, def, name, name) < 0) + { return NULL; } PyObject *modules = get_modules_dict(tstate, true); From 5627ad973164a25e2308b55c4139a41060db3a6d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 11:24:58 -0600 Subject: [PATCH 10/11] Update the comment about extensions. --- Python/import.c | 104 +++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/Python/import.c b/Python/import.c index 5331657a9bf34d..c1162ac88956f3 100644 --- a/Python/import.c +++ b/Python/import.c @@ -631,77 +631,91 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp) ...for single-phase init modules, where m_size == -1: (6). first time (not found in _PyRuntime.imports.extensions): - 1. _imp_create_dynamic_impl() -> import_find_extension() - 2. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() - 3. _PyImport_LoadDynamicModuleWithSpec(): load - 4. _PyImport_LoadDynamicModuleWithSpec(): call - 5. -> PyModule_Create() -> PyModule_Create2() -> PyModule_CreateInitialized() - 6. PyModule_CreateInitialized() -> PyModule_New() - 7. PyModule_CreateInitialized(): allocate mod->md_state - 8. PyModule_CreateInitialized() -> PyModule_AddFunctions() - 9. PyModule_CreateInitialized() -> PyModule_SetDocString() - 10. PyModule_CreateInitialized(): set mod->md_def - 11. : initialize the module - 12. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() - 13. _PyImport_LoadDynamicModuleWithSpec(): set def->m_base.m_init - 14. _PyImport_LoadDynamicModuleWithSpec(): set __file__ - 15. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_FixupExtensionObject() - 16. _PyImport_FixupExtensionObject(): add it to interp->imports.modules_by_index - 17. _PyImport_FixupExtensionObject(): copy __dict__ into def->m_base.m_copy - 18. _PyImport_FixupExtensionObject(): add it to _PyRuntime.imports.extensions + A. _imp_create_dynamic_impl() -> import_find_extension() + B. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() + C. _PyImport_LoadDynamicModuleWithSpec(): load + D. _PyImport_LoadDynamicModuleWithSpec(): call + E. -> PyModule_Create() -> PyModule_Create2() + -> PyModule_CreateInitialized() + F. PyModule_CreateInitialized() -> PyModule_New() + G. PyModule_CreateInitialized(): allocate mod->md_state + H. PyModule_CreateInitialized() -> PyModule_AddFunctions() + I. PyModule_CreateInitialized() -> PyModule_SetDocString() + J. PyModule_CreateInitialized(): set mod->md_def + K. : initialize the module, etc. + L. _PyImport_LoadDynamicModuleWithSpec() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + M. _PyImport_LoadDynamicModuleWithSpec(): set def->m_base.m_init + N. _PyImport_LoadDynamicModuleWithSpec() -> _PyImport_FixupExtensionObject() + O. _PyImport_FixupExtensionObject() -> update_global_state_for_extension() + P. update_global_state_for_extension(): + copy __dict__ into def->m_base.m_copy + Q. update_global_state_for_extension(): + add it to _PyRuntime.imports.extensions + R. _PyImport_FixupExtensionObject() -> finish_singlephase_extension() + S. finish_singlephase_extension(): + add it to interp->imports.modules_by_index + T. finish_singlephase_extension(): add it to sys.modules + U. _imp_create_dynamic_impl(): set __file__ + + Step (P) is skipped for core modules (sys/builtins). (6). subsequent times (found in _PyRuntime.imports.extensions): - 1. _imp_create_dynamic_impl() -> import_find_extension() - 2. import_find_extension() -> import_add_module() - 3. if name in sys.modules: use that module - 4. else: + A. _imp_create_dynamic_impl() -> import_find_extension() + B. import_find_extension() -> import_add_module() + C. if name in sys.modules: use that module + D. else: 1. import_add_module() -> PyModule_NewObject() 2. import_add_module(): set it on sys.modules - 5. import_find_extension(): copy the "m_copy" dict into __dict__ - 6. _imp_create_dynamic_impl() -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() + E. import_find_extension(): copy the "m_copy" dict into __dict__ + F. _imp_create_dynamic_impl() + -> _PyImport_CheckSubinterpIncompatibleExtensionAllowed() (10). (every time): - 1. noop + A. noop ...for single-phase init modules, where m_size >= 0: (6). not main interpreter and never loaded there - every time (not found in _PyRuntime.imports.extensions): - 1-16. (same as for m_size == -1) + A-N. (same as for m_size == -1) + O-Q. (skipped) + R-U. (same as for m_size == -1) (6). main interpreter - first time (not found in _PyRuntime.imports.extensions): - 1-16. (same as for m_size == -1) - 17. _PyImport_FixupExtensionObject(): add it to _PyRuntime.imports.extensions + A-O. (same as for m_size == -1) + P. (skipped) + Q-U. (same as for m_size == -1) (6). previously loaded in main interpreter (found in _PyRuntime.imports.extensions): - 1. _imp_create_dynamic_impl() -> import_find_extension() - 2. import_find_extension(): call def->m_base.m_init - 3. import_find_extension(): add the module to sys.modules + A. _imp_create_dynamic_impl() -> import_find_extension() + B. import_find_extension(): call def->m_base.m_init + C. import_find_extension(): add the module to sys.modules (10). every time: - 1. noop + A. noop ...for multi-phase init modules: (6). every time: - 1. _imp_create_dynamic_impl() -> import_find_extension() (not found) - 2. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() - 3. _PyImport_LoadDynamicModuleWithSpec(): load module init func - 4. _PyImport_LoadDynamicModuleWithSpec(): call module init func - 5. _PyImport_LoadDynamicModuleWithSpec() -> PyModule_FromDefAndSpec() - 6. PyModule_FromDefAndSpec(): gather/check moduledef slots - 7. if there's a Py_mod_create slot: + A. _imp_create_dynamic_impl() -> import_find_extension() (not found) + B. _imp_create_dynamic_impl() -> _PyImport_LoadDynamicModuleWithSpec() + C. _PyImport_LoadDynamicModuleWithSpec(): load module init func + D. _PyImport_LoadDynamicModuleWithSpec(): call module init func + E. _PyImport_LoadDynamicModuleWithSpec() -> PyModule_FromDefAndSpec() + F. PyModule_FromDefAndSpec(): gather/check moduledef slots + G. if there's a Py_mod_create slot: 1. PyModule_FromDefAndSpec(): call its function - 8. else: + H. else: 1. PyModule_FromDefAndSpec() -> PyModule_NewObject() - 9: PyModule_FromDefAndSpec(): set mod->md_def - 10. PyModule_FromDefAndSpec() -> _add_methods_to_object() - 11. PyModule_FromDefAndSpec() -> PyModule_SetDocString() + I: PyModule_FromDefAndSpec(): set mod->md_def + J. PyModule_FromDefAndSpec() -> _add_methods_to_object() + K. PyModule_FromDefAndSpec() -> PyModule_SetDocString() (10). every time: - 1. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() - 2. if mod->md_state == NULL (including if m_size == 0): + A. _imp_exec_dynamic_impl() -> exec_builtin_or_dynamic() + B. if mod->md_state == NULL (including if m_size == 0): 1. exec_builtin_or_dynamic() -> PyModule_ExecDef() 2. PyModule_ExecDef(): allocate mod->md_state 3. if there's a Py_mod_exec slot: From 8b4e216dfa45d84eae952ddde4010908adb9220f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 23 Apr 2024 11:30:24 -0600 Subject: [PATCH 11/11] Make sure __file__ is set before m_copy gets populated. --- Python/import.c | 6 ------ Python/importdl.c | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Python/import.c b/Python/import.c index c1162ac88956f3..30d8082607ab37 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3848,12 +3848,6 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) } mod = _PyImport_LoadDynamicModuleWithSpec(spec, fp); - if (mod != NULL) { - /* Remember the filename as the __file__ attribute */ - if (PyModule_AddObjectRef(mod, "__file__", filename) < 0) { - PyErr_Clear(); /* Not important enough to report */ - } - } // XXX Shouldn't this happen in the error cases too. if (fp) { diff --git a/Python/importdl.c b/Python/importdl.c index 7cf30bea3a861a..e512161d3071f2 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -226,6 +226,11 @@ _PyImport_LoadDynamicModuleWithSpec(PyObject *spec, FILE *fp) } def->m_base.m_init = p0; + /* Remember the filename as the __file__ attribute */ + if (PyModule_AddObjectRef(m, "__file__", filename) < 0) { + PyErr_Clear(); /* Not important enough to report */ + } + PyObject *modules = PyImport_GetModuleDict(); if (_PyImport_FixupExtensionObject(m, name_unicode, filename, modules) < 0) goto error;