gh-117953: Split Up _PyImport_LoadDynamicModuleWithSpec() (gh-118203)

Basically, I've turned most of _PyImport_LoadDynamicModuleWithSpec() into two new functions (_PyImport_GetModInitFunc() and _PyImport_RunModInitFunc()) and moved the rest of it out into _imp_create_dynamic_impl().  There shouldn't be any changes in behavior.

This change makes some future changes simpler.  This is particularly relevant to potentially calling each module init function in the main interpreter first.  Thus the critical part of the PR is the addition of _PyImport_RunModInitFunc(), which is strictly focused on running the init func and validating the result.  A later PR will take it a step farther by capturing error information rather than raising exceptions.

FWIW, this change also helps readers by clarifying a bit more about what happens when an extension/builtin module is imported.
This commit is contained in:
Eric Snow 2024-04-29 09:29:07 -06:00 committed by GitHub
parent 23d0371bb9
commit 44f57a952e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 193 additions and 145 deletions

View file

@ -179,17 +179,12 @@ _Py_ext_module_loader_info_init_from_spec(
}
PyObject *
_PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info,
PyObject *spec, FILE *fp)
PyModInitFunction
_PyImport_GetModInitFunc(struct _Py_ext_module_loader_info *info,
FILE *fp)
{
PyObject *m = NULL;
const char *name_buf = PyBytes_AS_STRING(info->name_encoded);
const char *oldcontext;
dl_funcptr exportfunc;
PyModInitFunction p0;
PyModuleDef *def;
#ifdef MS_WINDOWS
exportfunc = _PyImport_FindSharedFuncptrWindows(
info->hook_prefix, name_buf, info->filename, fp);
@ -213,16 +208,29 @@ _PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info,
Py_DECREF(msg);
}
}
goto error;
return NULL;
}
p0 = (PyModInitFunction)exportfunc;
return (PyModInitFunction)exportfunc;
}
int
_PyImport_RunModInitFunc(PyModInitFunction p0,
struct _Py_ext_module_loader_info *info,
struct _Py_ext_module_loader_result *p_res)
{
struct _Py_ext_module_loader_result res = {0};
const char *name_buf = PyBytes_AS_STRING(info->name_encoded);
/* Call the module init function. */
/* Package context is needed for single-phase init */
oldcontext = _PyImport_SwapPackageContext(info->newcontext);
m = p0();
const char *oldcontext = _PyImport_SwapPackageContext(info->newcontext);
PyObject *m = p0();
_PyImport_SwapPackageContext(oldcontext);
/* Validate the result (and populate "res". */
if (m == NULL) {
if (!PyErr_Occurred()) {
PyErr_Format(
@ -236,9 +244,13 @@ _PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info,
PyExc_SystemError,
"initialization of %s raised unreported exception",
name_buf);
/* We would probably be correct to decref m here,
* but we weren't doing so before,
* so we stick with doing nothing. */
m = NULL;
goto error;
}
if (Py_IS_TYPE(m, NULL)) {
/* This can happen when a PyModuleDef is returned without calling
* PyModuleDef_Init on it
@ -246,55 +258,52 @@ _PyImport_LoadDynamicModuleWithSpec(struct _Py_ext_module_loader_info *info,
PyErr_Format(PyExc_SystemError,
"init function of %s returned uninitialized object",
name_buf);
/* Likewise, decref'ing here makes sense. However, the original
* code has a note about "prevent segfault in DECREF",
* so we play it safe and leave it alone. */
m = NULL; /* prevent segfault in DECREF */
goto error;
}
if (PyObject_TypeCheck(m, &PyModuleDef_Type)) {
return PyModule_FromDefAndSpec((PyModuleDef*)m, spec);
/* multi-phase init */
res.def = (PyModuleDef *)m;
/* Run PyModule_FromDefAndSpec() to finish loading the module. */
}
/* Fall back to single-phase init mechanism */
if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
goto error;
}
if (info->hook_prefix == nonascii_prefix) {
/* don't allow legacy init for non-ASCII module names */
else if (info->hook_prefix == nonascii_prefix) {
/* It should have been multi-phase init? */
/* Don't allow legacy init for non-ASCII module names. */
PyErr_Format(
PyExc_SystemError,
"initialization of %s did not return PyModuleDef",
name_buf);
goto error;
Py_DECREF(m);
return -1;
}
else {
/* single-phase init (legacy) */
res.module = m;
res.def = PyModule_GetDef(m);
if (res.def == NULL) {
PyErr_Clear();
PyErr_Format(PyExc_SystemError,
"initialization of %s did not return an extension "
"module", name_buf);
goto error;
}
}
/* Remember pointer to module init function. */
def = PyModule_GetDef(m);
if (def == NULL) {
PyErr_Format(PyExc_SystemError,
"initialization of %s did not return an extension "
"module", name_buf);
goto error;
}
def->m_base.m_init = p0;
/* Remember the filename as the __file__ attribute */
if (PyModule_AddObjectRef(m, "__file__", info->filename) < 0) {
PyErr_Clear(); /* Not important enough to report */
}
PyObject *modules = PyImport_GetModuleDict();
if (_PyImport_FixupExtensionObject(
m, info->name, info->filename, modules) < 0)
{
goto error;
}
return m;
assert(!PyErr_Occurred());
*p_res = res;
return 0;
error:
Py_XDECREF(m);
return NULL;
assert(PyErr_Occurred());
Py_CLEAR(res.module);
res.def = NULL;
*p_res = res;
return -1;
}
#endif /* HAVE_DYNAMIC_LOADING */