Merge remote-tracking branch 'upstream/main' into lazy

This commit is contained in:
Pablo Galindo 2025-12-06 15:40:02 +00:00
commit db151a5192
869 changed files with 45727 additions and 16994 deletions

View file

@ -3,6 +3,7 @@
#include "Python.h"
#include "pycore_audit.h" // _PySys_Audit()
#include "pycore_ceval.h"
#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION()
#include "pycore_hashtable.h" // _Py_hashtable_new_full()
#include "pycore_import.h" // _PyImport_BootstrapImp()
#include "pycore_initconfig.h" // _PyStatus_OK()
@ -324,13 +325,8 @@ PyImport_GetModule(PyObject *name)
if not, create a new one and insert it in the modules dictionary. */
static PyObject *
import_add_module(PyThreadState *tstate, PyObject *name)
import_add_module_lock_held(PyObject *modules, PyObject *name)
{
PyObject *modules = get_modules_dict(tstate, false);
if (modules == NULL) {
return NULL;
}
PyObject *m;
if (PyMapping_GetOptionalItem(modules, name, &m) < 0) {
return NULL;
@ -350,6 +346,21 @@ import_add_module(PyThreadState *tstate, PyObject *name)
return m;
}
static PyObject *
import_add_module(PyThreadState *tstate, PyObject *name)
{
PyObject *modules = get_modules_dict(tstate, false);
if (modules == NULL) {
return NULL;
}
PyObject *m;
Py_BEGIN_CRITICAL_SECTION(modules);
m = import_add_module_lock_held(modules, name);
Py_END_CRITICAL_SECTION();
return m;
}
PyObject *
PyImport_AddModuleRef(const char *name)
{
@ -687,8 +698,8 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
(6). first time (not found in _PyRuntime.imports.extensions):
A. _imp_create_dynamic_impl() -> import_find_extension()
B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc()
C. _PyImport_GetModInitFunc(): load <module init func>
B. _imp_create_dynamic_impl() -> _PyImport_GetModuleExportHooks()
C. _PyImport_GetModuleExportHooks(): load <module init func>
D. _imp_create_dynamic_impl() -> import_run_extension()
E. import_run_extension() -> _PyImport_RunModInitFunc()
F. _PyImport_RunModInitFunc(): call <module init func>
@ -758,16 +769,19 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
A. noop
...for multi-phase init modules:
...for multi-phase init modules from PyModInit_* (PyModuleDef):
(6). every time:
A. _imp_create_dynamic_impl() -> import_find_extension() (not found)
B. _imp_create_dynamic_impl() -> _PyImport_GetModInitFunc()
C. _PyImport_GetModInitFunc(): load <module init func>
B. _imp_create_dynamic_impl() -> _PyImport_GetModuleExportHooks()
C. _PyImport_GetModuleExportHooks(): load <module init func>
D. _imp_create_dynamic_impl() -> import_run_extension()
E. import_run_extension() -> _PyImport_RunModInitFunc()
F. _PyImport_RunModInitFunc(): call <module init func>
G. import_run_extension() -> PyModule_FromDefAndSpec()
PyModule_FromDefAndSpec():
H. PyModule_FromDefAndSpec(): gather/check moduledef slots
I. if there's a Py_mod_create slot:
1. PyModule_FromDefAndSpec(): call its function
@ -780,10 +794,29 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
(10). every time:
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
1. exec_builtin_or_dynamic() -> PyModule_Exec()
2. PyModule_Exec(): allocate mod->md_state
3. if there's a Py_mod_exec slot:
1. PyModule_ExecDef(): call its function
1. PyModule_Exec(): call its function
...for multi-phase init modules from PyModExport_* (slots array):
(6). every time:
A. _imp_create_dynamic_impl() -> import_find_extension() (not found)
B. _imp_create_dynamic_impl() -> _PyImport_GetModuleExportHooks()
C. _PyImport_GetModuleExportHooks(): load <module export func>
D. _imp_create_dynamic_impl() -> import_run_modexport()
E. import_run_modexport(): call <module init func>
F. import_run_modexport() -> PyModule_FromSlotsAndSpec()
G. PyModule_FromSlotsAndSpec(): create temporary PyModuleDef-like
H. PyModule_FromSlotsAndSpec() -> PyModule_FromDefAndSpec()
(PyModule_FromDefAndSpec behaves as for PyModInit_*, above)
(10). every time: as for PyModInit_*, above
*/
@ -797,7 +830,7 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
substitute this (if the name actually matches).
*/
_Py_thread_local const char *pkgcontext = NULL;
static _Py_thread_local const char *pkgcontext = NULL;
# undef PKGCONTEXT
# define PKGCONTEXT pkgcontext
@ -840,25 +873,19 @@ _PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val)
/* Common implementation for _imp.exec_dynamic and _imp.exec_builtin */
static int
exec_builtin_or_dynamic(PyObject *mod) {
PyModuleDef *def;
void *state;
if (!PyModule_Check(mod)) {
return 0;
}
def = PyModule_GetDef(mod);
if (def == NULL) {
return 0;
}
state = PyModule_GetState(mod);
if (state) {
/* Already initialized; skip reload */
return 0;
}
return PyModule_ExecDef(mod, def);
return PyModule_Exec(mod);
}
@ -1016,9 +1043,10 @@ struct extensions_cache_value {
_Py_ext_module_origin origin;
#ifdef Py_GIL_DISABLED
/* The module's md_gil slot, for legacy modules that are reinitialized from
m_dict rather than calling their initialization function again. */
void *md_gil;
/* The module's md_requires_gil member, for legacy modules that are
* reinitialized from m_dict rather than calling their initialization
* function again. */
bool md_requires_gil;
#endif
};
@ -1349,7 +1377,7 @@ static struct extensions_cache_value *
_extensions_cache_set(PyObject *path, PyObject *name,
PyModuleDef *def, PyModInitFunction m_init,
Py_ssize_t m_index, PyObject *m_dict,
_Py_ext_module_origin origin, void *md_gil)
_Py_ext_module_origin origin, bool requires_gil)
{
struct extensions_cache_value *value = NULL;
void *key = NULL;
@ -1404,11 +1432,11 @@ _extensions_cache_set(PyObject *path, PyObject *name,
/* m_dict is set by set_cached_m_dict(). */
.origin=origin,
#ifdef Py_GIL_DISABLED
.md_gil=md_gil,
.md_requires_gil=requires_gil,
#endif
};
#ifndef Py_GIL_DISABLED
(void)md_gil;
(void)requires_gil;
#endif
if (init_cached_m_dict(newvalue, m_dict) < 0) {
goto finally;
@ -1546,26 +1574,13 @@ _PyImport_CheckGILForModule(PyObject* module, PyObject *module_name)
}
if (!PyModule_Check(module) ||
((PyModuleObject *)module)->md_gil == Py_MOD_GIL_USED) {
if (_PyEval_EnableGILPermanent(tstate)) {
int warn_result = PyErr_WarnFormat(
PyExc_RuntimeWarning,
1,
"The global interpreter lock (GIL) has been enabled to load "
"module '%U', which has not declared that it can run safely "
"without the GIL. To override this behavior and keep the GIL "
"disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.",
module_name
);
if (warn_result < 0) {
return warn_result;
}
((PyModuleObject *)module)->md_requires_gil)
{
if (PyModule_Check(module)) {
assert(((PyModuleObject *)module)->md_token_is_def);
}
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
if (config->enable_gil == _PyConfig_GIL_DEFAULT && config->verbose) {
PySys_FormatStderr("# loading module '%U', which requires the GIL\n",
module_name);
if (_PyImport_EnableGILAndWarn(tstate, module_name) < 0) {
return -1;
}
}
else {
@ -1574,6 +1589,28 @@ _PyImport_CheckGILForModule(PyObject* module, PyObject *module_name)
return 0;
}
int
_PyImport_EnableGILAndWarn(PyThreadState *tstate, PyObject *module_name)
{
if (_PyEval_EnableGILPermanent(tstate)) {
return PyErr_WarnFormat(
PyExc_RuntimeWarning,
1,
"The global interpreter lock (GIL) has been enabled to load "
"module '%U', which has not declared that it can run safely "
"without the GIL. To override this behavior and keep the GIL "
"disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.",
module_name
);
}
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
if (config->enable_gil == _PyConfig_GIL_DEFAULT && config->verbose) {
PySys_FormatStderr("# loading module '%U', which requires the GIL\n",
module_name);
}
return 0;
}
#endif
static PyThreadState *
@ -1724,7 +1761,7 @@ struct singlephase_global_update {
Py_ssize_t m_index;
PyObject *m_dict;
_Py_ext_module_origin origin;
void *md_gil;
bool md_requires_gil;
};
static struct extensions_cache_value *
@ -1783,7 +1820,7 @@ update_global_state_for_extension(PyThreadState *tstate,
#endif
cached = _extensions_cache_set(
path, name, def, m_init, singlephase->m_index, m_dict,
singlephase->origin, singlephase->md_gil);
singlephase->origin, singlephase->md_requires_gil);
if (cached == NULL) {
// XXX Ignore this error? Doing so would effectively
// mark the module as not loadable.
@ -1802,7 +1839,7 @@ finish_singlephase_extension(PyThreadState *tstate, PyObject *mod,
PyObject *name, PyObject *modules)
{
assert(mod != NULL && PyModule_Check(mod));
assert(cached->def == _PyModule_GetDef(mod));
assert(cached->def == _PyModule_GetDefOrNull(mod));
Py_ssize_t index = _get_cached_module_index(cached);
if (_modules_by_index_set(tstate->interp, index, mod) < 0) {
@ -1872,7 +1909,7 @@ reload_singlephase_extension(PyThreadState *tstate,
if (def->m_base.m_copy != NULL) {
// For non-core modules, fetch the GIL slot that was stored by
// import_run_extension().
((PyModuleObject *)mod)->md_gil = cached->md_gil;
((PyModuleObject *)mod)->md_requires_gil = cached->md_requires_gil;
}
#endif
/* We can't set mod->md_def if it's missing,
@ -1880,8 +1917,8 @@ reload_singlephase_extension(PyThreadState *tstate,
* due to violating interpreter isolation.
* See the note in set_cached_m_dict().
* Until that is solved, we leave md_def set to NULL. */
assert(_PyModule_GetDef(mod) == NULL
|| _PyModule_GetDef(mod) == def);
assert(_PyModule_GetDefOrNull(mod) == NULL
|| _PyModule_GetDefOrNull(mod) == def);
}
else {
assert(cached->m_dict == NULL);
@ -1968,6 +2005,43 @@ import_find_extension(PyThreadState *tstate,
return mod;
}
static PyObject *
import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0,
struct _Py_ext_module_loader_info *info,
PyObject *spec)
{
/* This is like import_run_extension, but avoids interpreter switching
* and code for for single-phase modules.
*/
PyModuleDef_Slot *slots = ex0();
if (!slots) {
if (!PyErr_Occurred()) {
PyErr_Format(
PyExc_SystemError,
"slot export function for module %s failed without setting an exception",
info->name);
}
return NULL;
}
if (PyErr_Occurred()) {
PyErr_Format(
PyExc_SystemError,
"slot export function for module %s raised unreported exception",
info->name);
}
PyObject *result = PyModule_FromSlotsAndSpec(slots, spec);
if (!result) {
return NULL;
}
if (PyModule_Check(result)) {
PyModuleObject *mod = (PyModuleObject *)result;
if (mod && !mod->md_token) {
mod->md_token = slots;
}
}
return result;
}
static PyObject *
import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
struct _Py_ext_module_loader_info *info,
@ -2090,7 +2164,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
.m_index=def->m_base.m_index,
.origin=info->origin,
#ifdef Py_GIL_DISABLED
.md_gil=((PyModuleObject *)mod)->md_gil,
.md_requires_gil=((PyModuleObject *)mod)->md_requires_gil,
#endif
};
// gh-88216: Extensions and def->m_base.m_copy can be updated
@ -2140,7 +2214,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
assert_multiphase_def(def);
assert(mod == NULL);
/* Note that we cheat a little by not repeating the calls
* to _PyImport_GetModInitFunc() and _PyImport_RunModInitFunc(). */
* to _PyImport_GetModuleExportHooks() and _PyImport_RunModInitFunc(). */
mod = PyModule_FromDefAndSpec(def, spec);
if (mod == NULL) {
goto error;
@ -2254,8 +2328,9 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name,
return -1;
}
PyModuleDef *def = PyModule_GetDef(mod);
PyModuleDef *def = _PyModule_GetDefOrNull(mod);
if (def == NULL) {
assert(!PyErr_Occurred());
PyErr_BadInternalCall();
goto finally;
}
@ -2284,7 +2359,7 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name,
.origin=_Py_ext_module_origin_CORE,
#ifdef Py_GIL_DISABLED
/* Unused when m_dict == NULL. */
.md_gil=NULL,
.md_requires_gil=false,
#endif
};
cached = update_global_state_for_extension(
@ -2323,8 +2398,23 @@ is_builtin(PyObject *name)
return 0;
}
static PyModInitFunction
lookup_inittab_initfunc(const struct _Py_ext_module_loader_info* info)
{
for (struct _inittab *p = INITTAB; p->name != NULL; p++) {
if (_PyUnicode_EqualToASCIIString(info->name, p->name)) {
return (PyModInitFunction)p->initfunc;
}
}
// not found
return NULL;
}
static PyObject*
create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
create_builtin(
PyThreadState *tstate, PyObject *name,
PyObject *spec,
PyModInitFunction initfunc)
{
struct _Py_ext_module_loader_info info;
if (_Py_ext_module_loader_info_init_for_builtin(&info, name) < 0) {
@ -2337,8 +2427,8 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
assert(!_PyErr_Occurred(tstate));
assert(cached != NULL);
/* The module might not have md_def set in certain reload cases. */
assert(_PyModule_GetDef(mod) == NULL
|| cached->def == _PyModule_GetDef(mod));
assert(_PyModule_GetDefOrNull(mod) == NULL
|| cached->def == _PyModule_GetDefOrNull(mod));
assert_singlephase(cached);
goto finally;
}
@ -2355,25 +2445,15 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
_extensions_cache_delete(info.path, info.name);
}
struct _inittab *found = NULL;
for (struct _inittab *p = INITTAB; p->name != NULL; p++) {
if (_PyUnicode_EqualToASCIIString(info.name, p->name)) {
found = p;
break;
}
}
if (found == NULL) {
// not found
mod = Py_NewRef(Py_None);
goto finally;
}
PyModInitFunction p0 = (PyModInitFunction)found->initfunc;
PyModInitFunction p0 = initfunc;
if (p0 == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
assert(is_core_module(tstate->interp, info.name, info.path));
mod = import_add_module(tstate, info.name);
goto finally;
p0 = lookup_inittab_initfunc(&info);
if (p0 == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
assert(is_core_module(tstate->interp, info.name, info.path));
mod = import_add_module(tstate, info.name);
goto finally;
}
}
#ifdef Py_GIL_DISABLED
@ -2399,6 +2479,33 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
return mod;
}
PyObject*
PyImport_CreateModuleFromInitfunc(
PyObject *spec, PyObject *(*initfunc)(void))
{
if (initfunc == NULL) {
PyErr_BadInternalCall();
return NULL;
}
PyThreadState *tstate = _PyThreadState_GET();
PyObject *name = PyObject_GetAttr(spec, &_Py_ID(name));
if (name == NULL) {
return NULL;
}
if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError,
"spec name must be string, not %T", name);
Py_DECREF(name);
return NULL;
}
PyObject *mod = create_builtin(tstate, name, spec, initfunc);
Py_DECREF(name);
return mod;
}
/*****************************/
/* the builtin modules table */
@ -3168,7 +3275,7 @@ bootstrap_imp(PyThreadState *tstate)
}
// Create the _imp module from its definition.
PyObject *mod = create_builtin(tstate, name, spec);
PyObject *mod = create_builtin(tstate, name, spec, NULL);
Py_CLEAR(name);
Py_DECREF(spec);
if (mod == NULL) {
@ -4798,7 +4905,7 @@ _imp_create_builtin(PyObject *module, PyObject *spec)
return NULL;
}
PyObject *mod = create_builtin(tstate, name, spec);
PyObject *mod = create_builtin(tstate, name, spec, NULL);
Py_DECREF(name);
return mod;
}
@ -5138,8 +5245,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
assert(!_PyErr_Occurred(tstate));
assert(cached != NULL);
/* The module might not have md_def set in certain reload cases. */
assert(_PyModule_GetDef(mod) == NULL
|| cached->def == _PyModule_GetDef(mod));
assert(_PyModule_GetDefOrNull(mod) == NULL
|| cached->def == _PyModule_GetDefOrNull(mod));
assert_singlephase(cached);
goto finally;
}
@ -5164,7 +5271,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
}
/* We would move this (and the fclose() below) into
* _PyImport_GetModInitFunc(), but it isn't clear if the intervening
* _PyImport_GetModuleExportHooks(), but it isn't clear if the intervening
* code relies on fp still being open. */
FILE *fp;
if (file != NULL) {
@ -5177,7 +5284,15 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
fp = NULL;
}
PyModInitFunction p0 = _PyImport_GetModInitFunc(&info, fp);
PyModInitFunction p0 = NULL;
PyModExportFunction ex0 = NULL;
_PyImport_GetModuleExportHooks(&info, fp, &p0, &ex0);
if (ex0) {
mod = import_run_modexport(tstate, ex0, &info, spec);
// Modules created from slots handle GIL enablement (Py_mod_gil slot)
// when they're created.
goto cleanup;
}
if (p0 == NULL) {
goto finally;
}
@ -5199,6 +5314,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
}
#endif
cleanup:
// XXX Shouldn't this happen in the error cases too (i.e. in "finally")?
if (fp) {
fclose(fp);