bpo-1635741: Convert _imp to multi-phase init (GH-23378)

Convert the _imp extension module to the multi-phase initialization
API (PEP 489).

* Add _PyImport_BootstrapImp() which fix a bootstrap issue: import
  the _imp module before importlib is initialized.
* Add create_builtin() sub-function, used by _imp_create_builtin().
* Initialize PyInterpreterState.import_func earlier, in
  pycore_init_builtins().
* Remove references to _PyImport_Cleanup(). This function has been
  renamed to finalize_modules() and moved to pylifecycle.c.
This commit is contained in:
Victor Stinner 2020-11-18 23:18:29 +01:00 committed by GitHub
parent e0251787d8
commit 6223071421
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 93 deletions

View File

@ -13,7 +13,7 @@ PyAPI_FUNC(PyObject *) _PyImport_FindBuiltin(
#ifdef HAVE_FORK #ifdef HAVE_FORK
extern PyStatus _PyImport_ReInitLock(void); extern PyStatus _PyImport_ReInitLock(void);
#endif #endif
extern void _PyImport_Cleanup(PyThreadState *tstate); extern PyObject* _PyImport_BootstrapImp(PyThreadState *tstate);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -14501,7 +14501,7 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie)
os.waitstatus_to_exitcode() is implemented in C and not in Python, so os.waitstatus_to_exitcode() is implemented in C and not in Python, so
subprocess can safely call it during late Python finalization without subprocess can safely call it during late Python finalization without
risking that used os attributes were set to None by _PyImport_Cleanup(). */ risking that used os attributes were set to None by finalize_modules(). */
#if defined(WIFEXITED) || defined(MS_WINDOWS) #if defined(WIFEXITED) || defined(MS_WINDOWS)
/*[clinic input] /*[clinic input]
os.waitstatus_to_exitcode os.waitstatus_to_exitcode

View File

@ -4,6 +4,7 @@
#include "Python-ast.h" #include "Python-ast.h"
#undef Yield /* undefine macro conflicting with <winbase.h> */ #undef Yield /* undefine macro conflicting with <winbase.h> */
#include "pycore_import.h" // _PyImport_BootstrapImp()
#include "pycore_initconfig.h" #include "pycore_initconfig.h"
#include "pycore_pyerrors.h" #include "pycore_pyerrors.h"
#include "pycore_pyhash.h" #include "pycore_pyhash.h"
@ -978,6 +979,54 @@ PyImport_GetImporter(PyObject *path)
return importer; return importer;
} }
static PyObject*
create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
{
PyObject *mod = _PyImport_FindExtensionObject(name, name);
if (mod || _PyErr_Occurred(tstate)) {
Py_XINCREF(mod);
return mod;
}
PyObject *modules = tstate->interp->modules;
for (struct _inittab *p = PyImport_Inittab; p->name != NULL; p++) {
if (_PyUnicode_EqualToASCIIString(name, p->name)) {
if (p->initfunc == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
return PyImport_AddModuleObject(name);
}
mod = (*p->initfunc)();
if (mod == NULL) {
return NULL;
}
if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) {
return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec);
}
else {
/* Remember pointer to module init function. */
PyModuleDef *def = PyModule_GetDef(mod);
if (def == NULL) {
return NULL;
}
def->m_base.m_init = p->initfunc;
if (_PyImport_FixupExtensionObject(mod, name, name,
modules) < 0) {
return NULL;
}
return mod;
}
}
}
// not found
Py_RETURN_NONE;
}
/*[clinic input] /*[clinic input]
_imp.create_builtin _imp.create_builtin
@ -992,68 +1041,16 @@ _imp_create_builtin(PyObject *module, PyObject *spec)
/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/ /*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyThreadState *tstate = _PyThreadState_GET();
struct _inittab *p;
PyObject *name;
const char *namestr;
PyObject *mod;
name = PyObject_GetAttrString(spec, "name"); PyObject *name = PyObject_GetAttrString(spec, "name");
if (name == NULL) { if (name == NULL) {
return NULL; return NULL;
} }
mod = _PyImport_FindExtensionObject(name, name); PyObject *mod = create_builtin(tstate, name, spec);
if (mod || _PyErr_Occurred(tstate)) {
Py_DECREF(name);
Py_XINCREF(mod);
return mod;
}
namestr = PyUnicode_AsUTF8(name);
if (namestr == NULL) {
Py_DECREF(name);
return NULL;
}
PyObject *modules = tstate->interp->modules;
for (p = PyImport_Inittab; p->name != NULL; p++) {
PyModuleDef *def;
if (_PyUnicode_EqualToASCIIString(name, p->name)) {
if (p->initfunc == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
mod = PyImport_AddModule(namestr);
Py_DECREF(name); Py_DECREF(name);
return mod; return mod;
} }
mod = (*p->initfunc)();
if (mod == NULL) {
Py_DECREF(name);
return NULL;
}
if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) {
Py_DECREF(name);
return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec);
} else {
/* Remember pointer to module init function. */
def = PyModule_GetDef(mod);
if (def == NULL) {
Py_DECREF(name);
return NULL;
}
def->m_base.m_init = p->initfunc;
if (_PyImport_FixupExtensionObject(mod, name, name,
modules) < 0) {
Py_DECREF(name);
return NULL;
}
Py_DECREF(name);
return mod;
}
}
}
Py_DECREF(name);
Py_RETURN_NONE;
}
/* Frozen modules */ /* Frozen modules */
@ -2127,46 +2124,88 @@ static PyMethodDef imp_methods[] = {
}; };
static struct PyModuleDef impmodule = { static int
imp_module_exec(PyObject *module)
{
const wchar_t *mode = _Py_GetConfig()->check_hash_pycs_mode;
PyObject *pyc_mode = PyUnicode_FromWideChar(mode, -1);
if (pyc_mode == NULL) {
return -1;
}
if (PyModule_AddObjectRef(module, "check_hash_based_pycs", pyc_mode) < 0) {
Py_DECREF(pyc_mode);
return -1;
}
Py_DECREF(pyc_mode);
return 0;
}
static PyModuleDef_Slot imp_slots[] = {
{Py_mod_exec, imp_module_exec},
{0, NULL}
};
static struct PyModuleDef imp_module = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"_imp", .m_name = "_imp",
doc_imp, .m_doc = doc_imp,
0, .m_size = 0,
imp_methods, .m_methods = imp_methods,
NULL, .m_slots = imp_slots,
NULL,
NULL,
NULL
}; };
PyMODINIT_FUNC PyMODINIT_FUNC
PyInit__imp(void) PyInit__imp(void)
{ {
PyObject *m, *d; return PyModuleDef_Init(&imp_module);
m = PyModule_Create(&impmodule);
if (m == NULL) {
goto failure;
}
d = PyModule_GetDict(m);
if (d == NULL) {
goto failure;
} }
const wchar_t *mode = _Py_GetConfig()->check_hash_pycs_mode;
PyObject *pyc_mode = PyUnicode_FromWideChar(mode, -1);
if (pyc_mode == NULL) {
goto failure;
}
if (PyDict_SetItemString(d, "check_hash_based_pycs", pyc_mode) < 0) {
Py_DECREF(pyc_mode);
goto failure;
}
Py_DECREF(pyc_mode);
return m; // Import the _imp extension by calling manually _imp.create_builtin() and
failure: // _imp.exec_builtin() since importlib is not initialized yet. Initializing
Py_XDECREF(m); // importlib requires the _imp module: this function fix the bootstrap issue.
PyObject*
_PyImport_BootstrapImp(PyThreadState *tstate)
{
PyObject *name = PyUnicode_FromString("_imp");
if (name == NULL) {
return NULL;
}
// Mock a ModuleSpec object just good enough for PyModule_FromDefAndSpec():
// an object with just a name attribute.
//
// _imp.__spec__ is overriden by importlib._bootstrap._instal() anyway.
PyObject *attrs = Py_BuildValue("{sO}", "name", name);
if (attrs == NULL) {
goto error;
}
PyObject *spec = _PyNamespace_New(attrs);
Py_DECREF(attrs);
if (spec == NULL) {
goto error;
}
// Create the _imp module from its definition.
PyObject *mod = create_builtin(tstate, name, spec);
Py_CLEAR(name);
Py_DECREF(spec);
if (mod == NULL) {
goto error;
}
assert(mod != Py_None); // not found
// Execute the _imp module: call imp_module_exec().
if (exec_builtin_or_dynamic(mod) < 0) {
Py_DECREF(mod);
goto error;
}
return mod;
error:
Py_XDECREF(name);
return NULL; return NULL;
} }

View File

@ -8,6 +8,7 @@
#include "pycore_ceval.h" // _PyEval_FiniGIL() #include "pycore_ceval.h" // _PyEval_FiniGIL()
#include "pycore_context.h" // _PyContext_Init() #include "pycore_context.h" // _PyContext_Init()
#include "pycore_fileutils.h" // _Py_ResetForceASCII() #include "pycore_fileutils.h" // _Py_ResetForceASCII()
#include "pycore_import.h" // _PyImport_BootstrapImp()
#include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_object.h" // _PyDebug_PrintTotalRefs() #include "pycore_object.h" // _PyDebug_PrintTotalRefs()
#include "pycore_pathconfig.h" // _PyConfig_WritePathConfig() #include "pycore_pathconfig.h" // _PyConfig_WritePathConfig()
@ -155,18 +156,11 @@ init_importlib(PyThreadState *tstate, PyObject *sysmod)
} }
interp->importlib = Py_NewRef(importlib); interp->importlib = Py_NewRef(importlib);
PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins,
"__import__");
if (import_func == NULL) {
return -1;
}
interp->import_func = Py_NewRef(import_func);
// Import the _imp module // Import the _imp module
if (verbose) { if (verbose) {
PySys_FormatStderr("import _imp # builtin\n"); PySys_FormatStderr("import _imp # builtin\n");
} }
PyObject *imp_mod = PyInit__imp(); PyObject *imp_mod = _PyImport_BootstrapImp(tstate);
if (imp_mod == NULL) { if (imp_mod == NULL) {
return -1; return -1;
} }
@ -741,6 +735,14 @@ pycore_init_builtins(PyThreadState *tstate)
} }
Py_DECREF(bimod); Py_DECREF(bimod);
// Get the __import__ function
PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins,
"__import__");
if (import_func == NULL) {
goto error;
}
interp->import_func = Py_NewRef(import_func);
assert(!_PyErr_Occurred(tstate)); assert(!_PyErr_Occurred(tstate));
return _PyStatus_OK(); return _PyStatus_OK();