https://github.com/python/cpython/commit/857bdba0ac66ed7963e9fca45ab1a5f7a32b4bfb
commit: 857bdba0ac66ed7963e9fca45ab1a5f7a32b4bfb
branch: main
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2025-02-18T18:02:42-05:00
summary:
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.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-02-14-00-32-52.gh-issue-130094.m3EF9E.rst
M Lib/importlib/_bootstrap.py
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index aa7efa1d509075..f5635265fbeebf 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -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:
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-14-00-32-52.gh-issue-130094.m3EF9E.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-14-00-32-52.gh-issue-130094.m3EF9E.rst
new file mode 100644
index 00000000000000..15d5831808df63
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-14-00-32-52.gh-issue-130094.m3EF9E.rst
@@ -0,0 +1,2 @@
+Fix two race conditions involving concurrent imports that could lead to
+spurious failures with :exc:`ModuleNotFoundError`.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]