mirror of
https://github.com/python/cpython.git
synced 2025-07-12 05:45:15 +00:00
Issue #22557: Now importing already imported modules is up to 2.5 times faster.
This commit is contained in:
parent
cde03fa038
commit
133138a284
6 changed files with 244 additions and 252 deletions
407
Python/import.c
407
Python/import.c
|
@ -1336,61 +1336,170 @@ done:
|
|||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
||||
PyObject *locals, PyObject *given_fromlist,
|
||||
int level)
|
||||
static PyObject *
|
||||
resolve_name(PyObject *name, PyObject *globals, int level)
|
||||
{
|
||||
_Py_IDENTIFIER(__import__);
|
||||
_Py_IDENTIFIER(__spec__);
|
||||
_Py_IDENTIFIER(_initializing);
|
||||
_Py_IDENTIFIER(__package__);
|
||||
_Py_IDENTIFIER(__path__);
|
||||
_Py_IDENTIFIER(__name__);
|
||||
_Py_IDENTIFIER(parent);
|
||||
PyObject *abs_name;
|
||||
PyObject *package = NULL;
|
||||
PyObject *spec;
|
||||
PyInterpreterState *interp = PyThreadState_GET()->interp;
|
||||
Py_ssize_t last_dot;
|
||||
PyObject *base;
|
||||
int level_up;
|
||||
|
||||
if (globals == NULL) {
|
||||
PyErr_SetString(PyExc_KeyError, "'__name__' not in globals");
|
||||
goto error;
|
||||
}
|
||||
if (!PyDict_Check(globals)) {
|
||||
PyErr_SetString(PyExc_TypeError, "globals must be a dict");
|
||||
goto error;
|
||||
}
|
||||
package = _PyDict_GetItemId(globals, &PyId___package__);
|
||||
if (package == Py_None) {
|
||||
package = NULL;
|
||||
}
|
||||
spec = _PyDict_GetItemId(globals, &PyId___spec__);
|
||||
|
||||
if (package != NULL) {
|
||||
Py_INCREF(package);
|
||||
if (!PyUnicode_Check(package)) {
|
||||
PyErr_SetString(PyExc_TypeError, "package must be a string");
|
||||
goto error;
|
||||
}
|
||||
else if (spec != NULL && spec != Py_None) {
|
||||
int equal;
|
||||
PyObject *parent = _PyObject_GetAttrId(spec, &PyId_parent);
|
||||
if (parent == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
equal = PyObject_RichCompareBool(package, parent, Py_EQ);
|
||||
Py_DECREF(parent);
|
||||
if (equal < 0) {
|
||||
goto error;
|
||||
}
|
||||
else if (equal == 0) {
|
||||
if (PyErr_WarnEx(PyExc_ImportWarning,
|
||||
"__package__ != __spec__.parent", 1) < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (spec != NULL && spec != Py_None) {
|
||||
package = _PyObject_GetAttrId(spec, &PyId_parent);
|
||||
if (package == NULL) {
|
||||
goto error;
|
||||
}
|
||||
else if (!PyUnicode_Check(package)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__spec__.parent must be a string");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (PyErr_WarnEx(PyExc_ImportWarning,
|
||||
"can't resolve package from __spec__ or __package__, "
|
||||
"falling back on __name__ and __path__", 1) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
package = _PyDict_GetItemId(globals, &PyId___name__);
|
||||
if (package == NULL) {
|
||||
PyErr_SetString(PyExc_KeyError, "'__name__' not in globals");
|
||||
goto error;
|
||||
}
|
||||
|
||||
Py_INCREF(package);
|
||||
if (!PyUnicode_Check(package)) {
|
||||
PyErr_SetString(PyExc_TypeError, "__name__ must be a string");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_PyDict_GetItemId(globals, &PyId___path__) == NULL) {
|
||||
Py_ssize_t dot;
|
||||
|
||||
if (PyUnicode_READY(package) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
dot = PyUnicode_FindChar(package, '.',
|
||||
0, PyUnicode_GET_LENGTH(package), -1);
|
||||
if (dot == -2) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dot >= 0) {
|
||||
PyObject *substr = PyUnicode_Substring(package, 0, dot);
|
||||
if (substr == NULL) {
|
||||
goto error;
|
||||
}
|
||||
Py_SETREF(package, substr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_dot = PyUnicode_GET_LENGTH(package);
|
||||
if (last_dot == 0) {
|
||||
PyErr_SetString(PyExc_ImportError,
|
||||
"attempted relative import with no known parent package");
|
||||
goto error;
|
||||
}
|
||||
else if (PyDict_GetItem(interp->modules, package) == NULL) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"Parent module %R not loaded, cannot perform relative "
|
||||
"import", package);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (level_up = 1; level_up < level; level_up += 1) {
|
||||
last_dot = PyUnicode_FindChar(package, '.', 0, last_dot, -1);
|
||||
if (last_dot == -2) {
|
||||
goto error;
|
||||
}
|
||||
else if (last_dot == -1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"attempted relative import beyond top-level "
|
||||
"package");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
base = PyUnicode_Substring(package, 0, last_dot);
|
||||
Py_DECREF(package);
|
||||
if (base == NULL || PyUnicode_GET_LENGTH(name) == 0) {
|
||||
return base;
|
||||
}
|
||||
|
||||
abs_name = PyUnicode_FromFormat("%U.%U", base, name);
|
||||
Py_DECREF(base);
|
||||
return abs_name;
|
||||
|
||||
error:
|
||||
Py_XDECREF(package);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
|
||||
PyObject *locals, PyObject *fromlist,
|
||||
int level)
|
||||
{
|
||||
_Py_IDENTIFIER(_find_and_load);
|
||||
_Py_IDENTIFIER(_handle_fromlist);
|
||||
_Py_IDENTIFIER(_lock_unlock_module);
|
||||
PyObject *abs_name = NULL;
|
||||
PyObject *builtins_import = NULL;
|
||||
PyObject *final_mod = NULL;
|
||||
PyObject *mod = NULL;
|
||||
PyObject *package = NULL;
|
||||
PyObject *spec = NULL;
|
||||
PyObject *globals = NULL;
|
||||
PyObject *fromlist = NULL;
|
||||
PyInterpreterState *interp = PyThreadState_GET()->interp;
|
||||
int has_from;
|
||||
|
||||
/* Make sure to use default values so as to not have
|
||||
PyObject_CallMethodObjArgs() truncate the parameter list because of a
|
||||
NULL argument. */
|
||||
if (given_globals == NULL) {
|
||||
globals = PyDict_New();
|
||||
if (globals == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Only have to care what given_globals is if it will be used
|
||||
for something. */
|
||||
if (level > 0 && !PyDict_Check(given_globals)) {
|
||||
PyErr_SetString(PyExc_TypeError, "globals must be a dict");
|
||||
goto error;
|
||||
}
|
||||
globals = given_globals;
|
||||
Py_INCREF(globals);
|
||||
}
|
||||
|
||||
if (given_fromlist == NULL) {
|
||||
fromlist = PyList_New(0);
|
||||
if (fromlist == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
fromlist = given_fromlist;
|
||||
Py_INCREF(fromlist);
|
||||
}
|
||||
if (name == NULL) {
|
||||
PyErr_SetString(PyExc_ValueError, "Empty module name");
|
||||
goto error;
|
||||
|
@ -1403,170 +1512,28 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
|||
PyErr_SetString(PyExc_TypeError, "module name must be a string");
|
||||
goto error;
|
||||
}
|
||||
else if (PyUnicode_READY(name) < 0) {
|
||||
if (PyUnicode_READY(name) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (level < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "level must be >= 0");
|
||||
goto error;
|
||||
}
|
||||
else if (level > 0) {
|
||||
package = _PyDict_GetItemId(globals, &PyId___package__);
|
||||
spec = _PyDict_GetItemId(globals, &PyId___spec__);
|
||||
|
||||
if (package != NULL && package != Py_None) {
|
||||
Py_INCREF(package);
|
||||
if (!PyUnicode_Check(package)) {
|
||||
PyErr_SetString(PyExc_TypeError, "package must be a string");
|
||||
goto error;
|
||||
}
|
||||
else if (spec != NULL && spec != Py_None) {
|
||||
int equal;
|
||||
PyObject *parent = PyObject_GetAttrString(spec, "parent");
|
||||
if (parent == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
equal = PyObject_RichCompareBool(package, parent, Py_EQ);
|
||||
Py_DECREF(parent);
|
||||
if (equal < 0) {
|
||||
goto error;
|
||||
}
|
||||
else if (equal == 0) {
|
||||
if (PyErr_WarnEx(PyExc_ImportWarning,
|
||||
"__package__ != __spec__.parent", 1) < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (spec != NULL && spec != Py_None) {
|
||||
package = PyObject_GetAttrString(spec, "parent");
|
||||
if (package == NULL) {
|
||||
goto error;
|
||||
}
|
||||
else if (!PyUnicode_Check(package)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__spec__.parent must be a string");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
package = NULL;
|
||||
if (PyErr_WarnEx(PyExc_ImportWarning,
|
||||
"can't resolve package from __spec__ or __package__, "
|
||||
"falling back on __name__ and __path__", 1) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
package = _PyDict_GetItemId(globals, &PyId___name__);
|
||||
if (package == NULL) {
|
||||
PyErr_SetString(PyExc_KeyError, "'__name__' not in globals");
|
||||
goto error;
|
||||
}
|
||||
|
||||
Py_INCREF(package);
|
||||
if (!PyUnicode_Check(package)) {
|
||||
PyErr_SetString(PyExc_TypeError, "__name__ must be a string");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_PyDict_GetItemId(globals, &PyId___path__) == NULL) {
|
||||
Py_ssize_t dot;
|
||||
|
||||
if (PyUnicode_READY(package) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
dot = PyUnicode_FindChar(package, '.',
|
||||
0, PyUnicode_GET_LENGTH(package), -1);
|
||||
if (dot == -2) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dot >= 0) {
|
||||
PyObject *substr = PyUnicode_Substring(package, 0, dot);
|
||||
if (substr == NULL) {
|
||||
goto error;
|
||||
}
|
||||
Py_SETREF(package, substr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
|
||||
PyErr_SetString(PyExc_ImportError,
|
||||
"attempted relative import with no known parent package");
|
||||
if (level > 0) {
|
||||
abs_name = resolve_name(name, globals, level);
|
||||
if (abs_name == NULL)
|
||||
goto error;
|
||||
}
|
||||
else if (PyDict_GetItem(interp->modules, package) == NULL) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"Parent module %R not loaded, cannot perform relative "
|
||||
"import", package);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else { /* level == 0 */
|
||||
if (PyUnicode_GET_LENGTH(name) == 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "Empty module name");
|
||||
goto error;
|
||||
}
|
||||
package = Py_None;
|
||||
Py_INCREF(package);
|
||||
}
|
||||
|
||||
if (level > 0) {
|
||||
Py_ssize_t last_dot = PyUnicode_GET_LENGTH(package);
|
||||
PyObject *base = NULL;
|
||||
int level_up = 1;
|
||||
|
||||
for (level_up = 1; level_up < level; level_up += 1) {
|
||||
last_dot = PyUnicode_FindChar(package, '.', 0, last_dot, -1);
|
||||
if (last_dot == -2) {
|
||||
goto error;
|
||||
}
|
||||
else if (last_dot == -1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"attempted relative import beyond top-level "
|
||||
"package");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
base = PyUnicode_Substring(package, 0, last_dot);
|
||||
if (base == NULL)
|
||||
goto error;
|
||||
|
||||
if (PyUnicode_GET_LENGTH(name) > 0) {
|
||||
abs_name = PyUnicode_FromFormat("%U.%U", base, name);
|
||||
Py_DECREF(base);
|
||||
if (abs_name == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
abs_name = base;
|
||||
}
|
||||
}
|
||||
else {
|
||||
abs_name = name;
|
||||
Py_INCREF(abs_name);
|
||||
}
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
_PyImport_AcquireLock();
|
||||
#endif
|
||||
/* From this point forward, goto error_with_unlock! */
|
||||
/* XXX interp->builtins_copy is NULL in subinterpreter! */
|
||||
builtins_import = _PyDict_GetItemId(interp->builtins_copy ?
|
||||
interp->builtins_copy :
|
||||
interp->builtins, &PyId___import__);
|
||||
if (builtins_import == NULL) {
|
||||
PyErr_SetString(PyExc_ImportError, "__import__ not found");
|
||||
goto error_with_unlock;
|
||||
}
|
||||
Py_INCREF(builtins_import);
|
||||
|
||||
mod = PyDict_GetItem(interp->modules, abs_name);
|
||||
if (mod == Py_None) {
|
||||
PyObject *msg = PyUnicode_FromFormat("import of %R halted; "
|
||||
|
@ -1576,9 +1543,12 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
|||
Py_DECREF(msg);
|
||||
}
|
||||
mod = NULL;
|
||||
goto error_with_unlock;
|
||||
goto error;
|
||||
}
|
||||
else if (mod != NULL) {
|
||||
_Py_IDENTIFIER(__spec__);
|
||||
_Py_IDENTIFIER(_initializing);
|
||||
_Py_IDENTIFIER(_lock_unlock_module);
|
||||
PyObject *value = NULL;
|
||||
PyObject *spec;
|
||||
int initializing = 0;
|
||||
|
@ -1601,39 +1571,39 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
|||
Py_DECREF(value);
|
||||
if (initializing == -1)
|
||||
PyErr_Clear();
|
||||
}
|
||||
if (initializing > 0) {
|
||||
/* _bootstrap._lock_unlock_module() releases the import lock */
|
||||
value = _PyObject_CallMethodIdObjArgs(interp->importlib,
|
||||
&PyId__lock_unlock_module, abs_name,
|
||||
NULL);
|
||||
if (value == NULL)
|
||||
goto error;
|
||||
Py_DECREF(value);
|
||||
}
|
||||
else {
|
||||
if (initializing > 0) {
|
||||
#ifdef WITH_THREAD
|
||||
if (_PyImport_ReleaseLock() < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "not holding the import lock");
|
||||
goto error;
|
||||
}
|
||||
_PyImport_AcquireLock();
|
||||
#endif
|
||||
/* _bootstrap._lock_unlock_module() releases the import lock */
|
||||
value = _PyObject_CallMethodIdObjArgs(interp->importlib,
|
||||
&PyId__lock_unlock_module, abs_name,
|
||||
NULL);
|
||||
if (value == NULL)
|
||||
goto error;
|
||||
Py_DECREF(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef WITH_THREAD
|
||||
_PyImport_AcquireLock();
|
||||
#endif
|
||||
/* _bootstrap._find_and_load() releases the import lock */
|
||||
mod = _PyObject_CallMethodIdObjArgs(interp->importlib,
|
||||
&PyId__find_and_load, abs_name,
|
||||
builtins_import, NULL);
|
||||
interp->import_func, NULL);
|
||||
if (mod == NULL) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
/* From now on we don't hold the import lock anymore. */
|
||||
|
||||
has_from = PyObject_IsTrue(fromlist);
|
||||
if (has_from < 0)
|
||||
goto error;
|
||||
has_from = 0;
|
||||
if (fromlist != NULL && fromlist != Py_None) {
|
||||
has_from = PyObject_IsTrue(fromlist);
|
||||
if (has_from < 0)
|
||||
goto error;
|
||||
}
|
||||
if (!has_from) {
|
||||
Py_ssize_t len = PyUnicode_GET_LENGTH(name);
|
||||
if (level == 0 || len > 0) {
|
||||
|
@ -1657,7 +1627,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
|||
goto error;
|
||||
}
|
||||
|
||||
final_mod = PyObject_CallFunctionObjArgs(builtins_import, front, NULL);
|
||||
final_mod = PyImport_ImportModuleLevelObject(front, NULL, NULL, NULL, 0);
|
||||
Py_DECREF(front);
|
||||
}
|
||||
else {
|
||||
|
@ -1670,15 +1640,14 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
|||
}
|
||||
|
||||
final_mod = PyDict_GetItem(interp->modules, to_return);
|
||||
Py_DECREF(to_return);
|
||||
if (final_mod == NULL) {
|
||||
PyErr_Format(PyExc_KeyError,
|
||||
"%R not in sys.modules as expected",
|
||||
to_return);
|
||||
goto error;
|
||||
}
|
||||
else {
|
||||
Py_INCREF(final_mod);
|
||||
}
|
||||
Py_DECREF(to_return);
|
||||
Py_INCREF(final_mod);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1689,24 +1658,14 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *given_globals,
|
|||
else {
|
||||
final_mod = _PyObject_CallMethodIdObjArgs(interp->importlib,
|
||||
&PyId__handle_fromlist, mod,
|
||||
fromlist, builtins_import,
|
||||
fromlist, interp->import_func,
|
||||
NULL);
|
||||
}
|
||||
goto error;
|
||||
|
||||
error_with_unlock:
|
||||
#ifdef WITH_THREAD
|
||||
if (_PyImport_ReleaseLock() < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "not holding the import lock");
|
||||
}
|
||||
#endif
|
||||
error:
|
||||
Py_XDECREF(abs_name);
|
||||
Py_XDECREF(builtins_import);
|
||||
Py_XDECREF(mod);
|
||||
Py_XDECREF(package);
|
||||
Py_XDECREF(globals);
|
||||
Py_XDECREF(fromlist);
|
||||
if (final_mod == NULL)
|
||||
remove_importlib_frames();
|
||||
return final_mod;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue