mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-130094: Fix race conditions in importlib
(gh-130101)
Entries may be added or removed from `sys.meta_path` concurrently. For example, setuptools temporarily adds and removes the `distutils` finder from the beginning of the list. The local copy ensures that we don't skip over any entries. Some packages modify `sys.modules` during import. For example, `collections` inserts the entry for `collections.abc` into `sys.modules` during import. We need to ensure that we re-check `sys.modules` *after* the parent module is fully initialized.
This commit is contained in:
parent
8207454bc0
commit
857bdba0ac
2 changed files with 14 additions and 3 deletions
|
@ -1244,6 +1244,9 @@ def _find_spec(name, path, target=None):
|
|||
raise ImportError("sys.meta_path is None, Python is likely "
|
||||
"shutting down")
|
||||
|
||||
# gh-130094: Copy sys.meta_path so that we have a consistent view of the
|
||||
# list while iterating over it.
|
||||
meta_path = list(meta_path)
|
||||
if not meta_path:
|
||||
_warnings.warn('sys.meta_path is empty', ImportWarning)
|
||||
|
||||
|
@ -1298,7 +1301,6 @@ def _sanity_check(name, package, level):
|
|||
|
||||
|
||||
_ERR_MSG_PREFIX = 'No module named '
|
||||
_ERR_MSG = _ERR_MSG_PREFIX + '{!r}'
|
||||
|
||||
def _find_and_load_unlocked(name, import_):
|
||||
path = None
|
||||
|
@ -1308,8 +1310,9 @@ def _find_and_load_unlocked(name, import_):
|
|||
if parent not in sys.modules:
|
||||
_call_with_frames_removed(import_, parent)
|
||||
# Crazy side-effects!
|
||||
if name in sys.modules:
|
||||
return sys.modules[name]
|
||||
module = sys.modules.get(name)
|
||||
if module is not None:
|
||||
return module
|
||||
parent_module = sys.modules[parent]
|
||||
try:
|
||||
path = parent_module.__path__
|
||||
|
@ -1317,6 +1320,12 @@ def _find_and_load_unlocked(name, import_):
|
|||
msg = f'{_ERR_MSG_PREFIX}{name!r}; {parent!r} is not a package'
|
||||
raise ModuleNotFoundError(msg, name=name) from None
|
||||
parent_spec = parent_module.__spec__
|
||||
if getattr(parent_spec, '_initializing', False):
|
||||
_call_with_frames_removed(import_, parent)
|
||||
# Crazy side-effects (again)!
|
||||
module = sys.modules.get(name)
|
||||
if module is not None:
|
||||
return module
|
||||
child = name.rpartition('.')[2]
|
||||
spec = _find_spec(name, path)
|
||||
if spec is None:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue