gh-117953: Track Extra Details in Global Extensions Cache (gh-118532)

We have only been tracking each module's PyModuleDef.  However, there are some problems with that.  For example, in some cases we load single-phase init extension modules from def->m_base.m_init or def->m_base.m_copy, but if multiple modules share a def then we can end up with unexpected behavior.

With this change, we track the following:

* PyModuleDef (same as before)
* for some modules, its init function or a copy of its __dict__, but specific to that module
* whether it is a builtin/core module or a "dynamic" extension
* the interpreter (ID) that owns the cached __dict__ (only if cached)

This also makes it easier to remember the module's kind (e.g. single-phase init) and if loading it previously failed, which I'm doing separately.
This commit is contained in:
Eric Snow 2024-05-04 15:24:02 -06:00 committed by GitHub
parent 978fba58ae
commit 291cfa454b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 577 additions and 137 deletions

View File

@ -22,6 +22,11 @@ typedef enum ext_module_kind {
_Py_ext_module_kind_INVALID = 3,
} _Py_ext_module_kind;
typedef enum ext_module_origin {
_Py_ext_module_origin_CORE = 1,
_Py_ext_module_origin_BUILTIN = 2,
_Py_ext_module_origin_DYNAMIC = 3,
} _Py_ext_module_origin;
/* Input for loading an extension module. */
struct _Py_ext_module_loader_info {
@ -34,6 +39,7 @@ struct _Py_ext_module_loader_info {
/* path is always a borrowed ref of name or filename,
* depending on if it's builtin or not. */
PyObject *path;
_Py_ext_module_origin origin;
const char *hook_prefix;
const char *newcontext;
};
@ -42,7 +48,11 @@ extern void _Py_ext_module_loader_info_clear(
extern int _Py_ext_module_loader_info_init(
struct _Py_ext_module_loader_info *info,
PyObject *name,
PyObject *filename);
PyObject *filename,
_Py_ext_module_origin origin);
extern int _Py_ext_module_loader_info_init_for_core(
struct _Py_ext_module_loader_info *p_info,
PyObject *name);
extern int _Py_ext_module_loader_info_init_for_builtin(
struct _Py_ext_module_loader_info *p_info,
PyObject *name);

File diff suppressed because it is too large Load Diff

View File

@ -111,9 +111,12 @@ _Py_ext_module_loader_info_clear(struct _Py_ext_module_loader_info *info)
int
_Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info,
PyObject *name, PyObject *filename)
PyObject *name, PyObject *filename,
_Py_ext_module_origin origin)
{
struct _Py_ext_module_loader_info info = {0};
struct _Py_ext_module_loader_info info = {
.origin=origin,
};
assert(name != NULL);
if (!PyUnicode_Check(name)) {
@ -183,12 +186,25 @@ _Py_ext_module_loader_info_init_for_builtin(
.name_encoded=name_encoded,
/* We won't need filename. */
.path=name,
.origin=_Py_ext_module_origin_BUILTIN,
.hook_prefix=ascii_only_prefix,
.newcontext=NULL,
};
return 0;
}
int
_Py_ext_module_loader_info_init_for_core(
struct _Py_ext_module_loader_info *info,
PyObject *name)
{
if (_Py_ext_module_loader_info_init_for_builtin(info, name) < 0) {
return -1;
}
info->origin = _Py_ext_module_origin_CORE;
return 0;
}
int
_Py_ext_module_loader_info_init_from_spec(
struct _Py_ext_module_loader_info *p_info,
@ -203,7 +219,9 @@ _Py_ext_module_loader_info_init_from_spec(
Py_DECREF(name);
return -1;
}
int err = _Py_ext_module_loader_info_init(p_info, name, filename);
/* We could also accommodate builtin modules here without much trouble. */
_Py_ext_module_origin origin = _Py_ext_module_origin_DYNAMIC;
int err = _Py_ext_module_loader_info_init(p_info, name, filename, origin);
Py_DECREF(name);
Py_DECREF(filename);
return err;