Skip to content

Commit 4e24425

Browse files
bpo-32946: Speed up "from ... import ..." from non-packages. (pythonGH-5873)
1 parent b931bd0 commit 4e24425

File tree

4 files changed

+302
-288
lines changed

4 files changed

+302
-288
lines changed

Lib/importlib/_bootstrap.py

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,31 +1016,30 @@ def _handle_fromlist(module, fromlist, import_, *, recursive=False):
10161016
"""
10171017
# The hell that is fromlist ...
10181018
# If a package was imported, try to import stuff from fromlist.
1019-
if hasattr(module, '__path__'):
1020-
for x in fromlist:
1021-
if not isinstance(x, str):
1022-
if recursive:
1023-
where = module.__name__ + '.__all__'
1024-
else:
1025-
where = "``from list''"
1026-
raise TypeError(f"Item in {where} must be str, "
1027-
f"not {type(x).__name__}")
1028-
elif x == '*':
1029-
if not recursive and hasattr(module, '__all__'):
1030-
_handle_fromlist(module, module.__all__, import_,
1031-
recursive=True)
1032-
elif not hasattr(module, x):
1033-
from_name = '{}.{}'.format(module.__name__, x)
1034-
try:
1035-
_call_with_frames_removed(import_, from_name)
1036-
except ModuleNotFoundError as exc:
1037-
# Backwards-compatibility dictates we ignore failed
1038-
# imports triggered by fromlist for modules that don't
1039-
# exist.
1040-
if (exc.name == from_name and
1041-
sys.modules.get(from_name, _NEEDS_LOADING) is not None):
1042-
continue
1043-
raise
1019+
for x in fromlist:
1020+
if not isinstance(x, str):
1021+
if recursive:
1022+
where = module.__name__ + '.__all__'
1023+
else:
1024+
where = "``from list''"
1025+
raise TypeError(f"Item in {where} must be str, "
1026+
f"not {type(x).__name__}")
1027+
elif x == '*':
1028+
if not recursive and hasattr(module, '__all__'):
1029+
_handle_fromlist(module, module.__all__, import_,
1030+
recursive=True)
1031+
elif not hasattr(module, x):
1032+
from_name = '{}.{}'.format(module.__name__, x)
1033+
try:
1034+
_call_with_frames_removed(import_, from_name)
1035+
except ModuleNotFoundError as exc:
1036+
# Backwards-compatibility dictates we ignore failed
1037+
# imports triggered by fromlist for modules that don't
1038+
# exist.
1039+
if (exc.name == from_name and
1040+
sys.modules.get(from_name, _NEEDS_LOADING) is not None):
1041+
continue
1042+
raise
10441043
return module
10451044

10461045

@@ -1102,8 +1101,10 @@ def __import__(name, globals=None, locals=None, fromlist=(), level=0):
11021101
# Slice end needs to be positive to alleviate need to special-case
11031102
# when ``'.' not in name``.
11041103
return sys.modules[module.__name__[:len(module.__name__)-cut_off]]
1105-
else:
1104+
elif hasattr(module, '__path__'):
11061105
return _handle_fromlist(module, fromlist, _gcd_import)
1106+
else:
1107+
return module
11071108

11081109

11091110
def _builtin_from_name(name):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Importing names from already imported module with "from ... import ..." is
2+
now 30% faster if the module is not a package.

Python/import.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,10 +1800,21 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
18001800
}
18011801
}
18021802
else {
1803-
final_mod = _PyObject_CallMethodIdObjArgs(interp->importlib,
1804-
&PyId__handle_fromlist, mod,
1805-
fromlist, interp->import_func,
1806-
NULL);
1803+
_Py_IDENTIFIER(__path__);
1804+
PyObject *path;
1805+
if (_PyObject_LookupAttrId(mod, &PyId___path__, &path) < 0) {
1806+
goto error;
1807+
}
1808+
if (path) {
1809+
Py_DECREF(path);
1810+
final_mod = _PyObject_CallMethodIdObjArgs(
1811+
interp->importlib, &PyId__handle_fromlist,
1812+
mod, fromlist, interp->import_func, NULL);
1813+
}
1814+
else {
1815+
final_mod = mod;
1816+
Py_INCREF(mod);
1817+
}
18071818
}
18081819

18091820
error:

0 commit comments

Comments
 (0)