Skip to content

Commit 857bdba

Browse files
authored
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.
1 parent 8207454 commit 857bdba

File tree

2 files changed

+14
-3
lines changed

2 files changed

+14
-3
lines changed

Lib/importlib/_bootstrap.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,9 @@ def _find_spec(name, path, target=None):
12441244
raise ImportError("sys.meta_path is None, Python is likely "
12451245
"shutting down")
12461246

1247+
# gh-130094: Copy sys.meta_path so that we have a consistent view of the
1248+
# list while iterating over it.
1249+
meta_path = list(meta_path)
12471250
if not meta_path:
12481251
_warnings.warn('sys.meta_path is empty', ImportWarning)
12491252

@@ -1298,7 +1301,6 @@ def _sanity_check(name, package, level):
12981301

12991302

13001303
_ERR_MSG_PREFIX = 'No module named '
1301-
_ERR_MSG = _ERR_MSG_PREFIX + '{!r}'
13021304

13031305
def _find_and_load_unlocked(name, import_):
13041306
path = None
@@ -1308,15 +1310,22 @@ def _find_and_load_unlocked(name, import_):
13081310
if parent not in sys.modules:
13091311
_call_with_frames_removed(import_, parent)
13101312
# Crazy side-effects!
1311-
if name in sys.modules:
1312-
return sys.modules[name]
1313+
module = sys.modules.get(name)
1314+
if module is not None:
1315+
return module
13131316
parent_module = sys.modules[parent]
13141317
try:
13151318
path = parent_module.__path__
13161319
except AttributeError:
13171320
msg = f'{_ERR_MSG_PREFIX}{name!r}; {parent!r} is not a package'
13181321
raise ModuleNotFoundError(msg, name=name) from None
13191322
parent_spec = parent_module.__spec__
1323+
if getattr(parent_spec, '_initializing', False):
1324+
_call_with_frames_removed(import_, parent)
1325+
# Crazy side-effects (again)!
1326+
module = sys.modules.get(name)
1327+
if module is not None:
1328+
return module
13201329
child = name.rpartition('.')[2]
13211330
spec = _find_spec(name, path)
13221331
if spec is None:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix two race conditions involving concurrent imports that could lead to
2+
spurious failures with :exc:`ModuleNotFoundError`.

0 commit comments

Comments
 (0)