Skip to content

Commit 6223071

Browse files
authored
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.
1 parent e025178 commit 6223071

File tree

4 files changed

+122
-81
lines changed

4 files changed

+122
-81
lines changed

Include/internal/pycore_import.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ PyAPI_FUNC(PyObject *) _PyImport_FindBuiltin(
1313
#ifdef HAVE_FORK
1414
extern PyStatus _PyImport_ReInitLock(void);
1515
#endif
16-
extern void _PyImport_Cleanup(PyThreadState *tstate);
16+
extern PyObject* _PyImport_BootstrapImp(PyThreadState *tstate);
1717

1818
#ifdef __cplusplus
1919
}

Modules/posixmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14501,7 +14501,7 @@ os__remove_dll_directory_impl(PyObject *module, PyObject *cookie)
1450114501
1450214502
os.waitstatus_to_exitcode() is implemented in C and not in Python, so
1450314503
subprocess can safely call it during late Python finalization without
14504-
risking that used os attributes were set to None by _PyImport_Cleanup(). */
14504+
risking that used os attributes were set to None by finalize_modules(). */
1450514505
#if defined(WIFEXITED) || defined(MS_WINDOWS)
1450614506
/*[clinic input]
1450714507
os.waitstatus_to_exitcode

Python/import.c

Lines changed: 110 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "Python-ast.h"
66
#undef Yield /* undefine macro conflicting with <winbase.h> */
7+
#include "pycore_import.h" // _PyImport_BootstrapImp()
78
#include "pycore_initconfig.h"
89
#include "pycore_pyerrors.h"
910
#include "pycore_pyhash.h"
@@ -978,84 +979,80 @@ PyImport_GetImporter(PyObject *path)
978979
return importer;
979980
}
980981

981-
/*[clinic input]
982-
_imp.create_builtin
983-
984-
spec: object
985-
/
986-
987-
Create an extension module.
988-
[clinic start generated code]*/
989-
990-
static PyObject *
991-
_imp_create_builtin(PyObject *module, PyObject *spec)
992-
/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/
982+
static PyObject*
983+
create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
993984
{
994-
PyThreadState *tstate = _PyThreadState_GET();
995-
struct _inittab *p;
996-
PyObject *name;
997-
const char *namestr;
998-
PyObject *mod;
999-
1000-
name = PyObject_GetAttrString(spec, "name");
1001-
if (name == NULL) {
1002-
return NULL;
1003-
}
1004-
1005-
mod = _PyImport_FindExtensionObject(name, name);
985+
PyObject *mod = _PyImport_FindExtensionObject(name, name);
1006986
if (mod || _PyErr_Occurred(tstate)) {
1007-
Py_DECREF(name);
1008987
Py_XINCREF(mod);
1009988
return mod;
1010989
}
1011990

1012-
namestr = PyUnicode_AsUTF8(name);
1013-
if (namestr == NULL) {
1014-
Py_DECREF(name);
1015-
return NULL;
1016-
}
1017-
1018991
PyObject *modules = tstate->interp->modules;
1019-
for (p = PyImport_Inittab; p->name != NULL; p++) {
1020-
PyModuleDef *def;
992+
for (struct _inittab *p = PyImport_Inittab; p->name != NULL; p++) {
1021993
if (_PyUnicode_EqualToASCIIString(name, p->name)) {
1022994
if (p->initfunc == NULL) {
1023995
/* Cannot re-init internal module ("sys" or "builtins") */
1024-
mod = PyImport_AddModule(namestr);
1025-
Py_DECREF(name);
1026-
return mod;
996+
return PyImport_AddModuleObject(name);
1027997
}
998+
1028999
mod = (*p->initfunc)();
10291000
if (mod == NULL) {
1030-
Py_DECREF(name);
10311001
return NULL;
10321002
}
1003+
10331004
if (PyObject_TypeCheck(mod, &PyModuleDef_Type)) {
1034-
Py_DECREF(name);
10351005
return PyModule_FromDefAndSpec((PyModuleDef*)mod, spec);
1036-
} else {
1006+
}
1007+
else {
10371008
/* Remember pointer to module init function. */
1038-
def = PyModule_GetDef(mod);
1009+
PyModuleDef *def = PyModule_GetDef(mod);
10391010
if (def == NULL) {
1040-
Py_DECREF(name);
10411011
return NULL;
10421012
}
1013+
10431014
def->m_base.m_init = p->initfunc;
10441015
if (_PyImport_FixupExtensionObject(mod, name, name,
10451016
modules) < 0) {
1046-
Py_DECREF(name);
10471017
return NULL;
10481018
}
1049-
Py_DECREF(name);
10501019
return mod;
10511020
}
10521021
}
10531022
}
1054-
Py_DECREF(name);
1023+
1024+
// not found
10551025
Py_RETURN_NONE;
10561026
}
10571027

10581028

1029+
1030+
/*[clinic input]
1031+
_imp.create_builtin
1032+
1033+
spec: object
1034+
/
1035+
1036+
Create an extension module.
1037+
[clinic start generated code]*/
1038+
1039+
static PyObject *
1040+
_imp_create_builtin(PyObject *module, PyObject *spec)
1041+
/*[clinic end generated code: output=ace7ff22271e6f39 input=37f966f890384e47]*/
1042+
{
1043+
PyThreadState *tstate = _PyThreadState_GET();
1044+
1045+
PyObject *name = PyObject_GetAttrString(spec, "name");
1046+
if (name == NULL) {
1047+
return NULL;
1048+
}
1049+
1050+
PyObject *mod = create_builtin(tstate, name, spec);
1051+
Py_DECREF(name);
1052+
return mod;
1053+
}
1054+
1055+
10591056
/* Frozen modules */
10601057

10611058
static const struct _frozen *
@@ -2127,46 +2124,88 @@ static PyMethodDef imp_methods[] = {
21272124
};
21282125

21292126

2130-
static struct PyModuleDef impmodule = {
2127+
static int
2128+
imp_module_exec(PyObject *module)
2129+
{
2130+
const wchar_t *mode = _Py_GetConfig()->check_hash_pycs_mode;
2131+
PyObject *pyc_mode = PyUnicode_FromWideChar(mode, -1);
2132+
if (pyc_mode == NULL) {
2133+
return -1;
2134+
}
2135+
if (PyModule_AddObjectRef(module, "check_hash_based_pycs", pyc_mode) < 0) {
2136+
Py_DECREF(pyc_mode);
2137+
return -1;
2138+
}
2139+
Py_DECREF(pyc_mode);
2140+
2141+
return 0;
2142+
}
2143+
2144+
2145+
static PyModuleDef_Slot imp_slots[] = {
2146+
{Py_mod_exec, imp_module_exec},
2147+
{0, NULL}
2148+
};
2149+
2150+
static struct PyModuleDef imp_module = {
21312151
PyModuleDef_HEAD_INIT,
2132-
"_imp",
2133-
doc_imp,
2134-
0,
2135-
imp_methods,
2136-
NULL,
2137-
NULL,
2138-
NULL,
2139-
NULL
2152+
.m_name = "_imp",
2153+
.m_doc = doc_imp,
2154+
.m_size = 0,
2155+
.m_methods = imp_methods,
2156+
.m_slots = imp_slots,
21402157
};
21412158

21422159
PyMODINIT_FUNC
21432160
PyInit__imp(void)
21442161
{
2145-
PyObject *m, *d;
2162+
return PyModuleDef_Init(&imp_module);
2163+
}
21462164

2147-
m = PyModule_Create(&impmodule);
2148-
if (m == NULL) {
2149-
goto failure;
2165+
2166+
// Import the _imp extension by calling manually _imp.create_builtin() and
2167+
// _imp.exec_builtin() since importlib is not initialized yet. Initializing
2168+
// importlib requires the _imp module: this function fix the bootstrap issue.
2169+
PyObject*
2170+
_PyImport_BootstrapImp(PyThreadState *tstate)
2171+
{
2172+
PyObject *name = PyUnicode_FromString("_imp");
2173+
if (name == NULL) {
2174+
return NULL;
21502175
}
2151-
d = PyModule_GetDict(m);
2152-
if (d == NULL) {
2153-
goto failure;
2176+
2177+
// Mock a ModuleSpec object just good enough for PyModule_FromDefAndSpec():
2178+
// an object with just a name attribute.
2179+
//
2180+
// _imp.__spec__ is overriden by importlib._bootstrap._instal() anyway.
2181+
PyObject *attrs = Py_BuildValue("{sO}", "name", name);
2182+
if (attrs == NULL) {
2183+
goto error;
2184+
}
2185+
PyObject *spec = _PyNamespace_New(attrs);
2186+
Py_DECREF(attrs);
2187+
if (spec == NULL) {
2188+
goto error;
21542189
}
21552190

2156-
const wchar_t *mode = _Py_GetConfig()->check_hash_pycs_mode;
2157-
PyObject *pyc_mode = PyUnicode_FromWideChar(mode, -1);
2158-
if (pyc_mode == NULL) {
2159-
goto failure;
2191+
// Create the _imp module from its definition.
2192+
PyObject *mod = create_builtin(tstate, name, spec);
2193+
Py_CLEAR(name);
2194+
Py_DECREF(spec);
2195+
if (mod == NULL) {
2196+
goto error;
21602197
}
2161-
if (PyDict_SetItemString(d, "check_hash_based_pycs", pyc_mode) < 0) {
2162-
Py_DECREF(pyc_mode);
2163-
goto failure;
2198+
assert(mod != Py_None); // not found
2199+
2200+
// Execute the _imp module: call imp_module_exec().
2201+
if (exec_builtin_or_dynamic(mod) < 0) {
2202+
Py_DECREF(mod);
2203+
goto error;
21642204
}
2165-
Py_DECREF(pyc_mode);
2205+
return mod;
21662206

2167-
return m;
2168-
failure:
2169-
Py_XDECREF(m);
2207+
error:
2208+
Py_XDECREF(name);
21702209
return NULL;
21712210
}
21722211

Python/pylifecycle.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "pycore_ceval.h" // _PyEval_FiniGIL()
99
#include "pycore_context.h" // _PyContext_Init()
1010
#include "pycore_fileutils.h" // _Py_ResetForceASCII()
11+
#include "pycore_import.h" // _PyImport_BootstrapImp()
1112
#include "pycore_initconfig.h" // _PyStatus_OK()
1213
#include "pycore_object.h" // _PyDebug_PrintTotalRefs()
1314
#include "pycore_pathconfig.h" // _PyConfig_WritePathConfig()
@@ -155,18 +156,11 @@ init_importlib(PyThreadState *tstate, PyObject *sysmod)
155156
}
156157
interp->importlib = Py_NewRef(importlib);
157158

158-
PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins,
159-
"__import__");
160-
if (import_func == NULL) {
161-
return -1;
162-
}
163-
interp->import_func = Py_NewRef(import_func);
164-
165159
// Import the _imp module
166160
if (verbose) {
167161
PySys_FormatStderr("import _imp # builtin\n");
168162
}
169-
PyObject *imp_mod = PyInit__imp();
163+
PyObject *imp_mod = _PyImport_BootstrapImp(tstate);
170164
if (imp_mod == NULL) {
171165
return -1;
172166
}
@@ -741,6 +735,14 @@ pycore_init_builtins(PyThreadState *tstate)
741735
}
742736
Py_DECREF(bimod);
743737

738+
// Get the __import__ function
739+
PyObject *import_func = _PyDict_GetItemStringWithError(interp->builtins,
740+
"__import__");
741+
if (import_func == NULL) {
742+
goto error;
743+
}
744+
interp->import_func = Py_NewRef(import_func);
745+
744746
assert(!_PyErr_Occurred(tstate));
745747

746748
return _PyStatus_OK();

0 commit comments

Comments
 (0)