Bug report
Bug description:
I found a regression in Python 3.15’s site.py startup-file processing. A .pth file containing an import line that calls site.addsitedir() can re-enter process_startup_files() while _exec_imports() is iterating _pending_importexecs, causing recursive processing of the same .pth file and eventually a startup failure.
This was originally observed with uv run --with ..., because uv creates an ephemeral overlay .pth file like:
import site; site.addsitedir("/path/to/overlay/site-packages"); site.addsitedir("/usr/local/lib/python3.15/site-packages")
But the issue can be reproduced without uv.
Environment
Python 3.15.0b1+ (heads/3.15:e81025e6d2e, May 7 2026, 10:29:46)
[Clang 15.0.0 (clang-1500.3.9.4)]
macOS arm64
Minimal Reproducer
mkdir -p /tmp/site-repro/root /tmp/site-repro/overlay
printf 'import site; site.addsitedir("/tmp/site-repro/overlay")\n' > /tmp/site-repro/root/reenter.pth
python3.15 -S -c 'import site; site.addsitedir("/tmp/site-repro/root"); print("done")'
Expected Behavior
The .pth import line should execute once, add /tmp/site-repro/overlay as a site directory, and the command should print:
Actual Behavior
Python repeatedly processes the same .pth import line, then fails with errors like:
Error in import line from /tmp/site-repro/root/reenter.pth: import site; site.addsitedir("/tmp/site-repro/overlay")
...
RuntimeError: dictionary changed size during iteration
In startup contexts, such as uv run --with ..., this can become fatal during site initialization:
Fatal Python error: init_import_site: Failed to import the site module
Python runtime state: core initialized
Traceback (most recent call last):
File "<frozen site>", line 901, in <module>
File "<frozen site>", line 877, in main
File "<frozen site>", line 812, in venv
File "<frozen site>", line 598, in addsitepackages
File "<frozen site>", line 422, in addsitedir
File "<frozen site>", line 359, in process_startup_files
File "<frozen site>", line 303, in _exec_imports
RuntimeError: dictionary changed size during iteration
Likely Cause
In Lib/site.py, addsitedir() calls process_startup_files() unless defer_processing_start_files=True.
process_startup_files() calls _exec_imports(), which iterates directly over _pending_importexecs.items():
def _exec_imports():
for filename, imports in _pending_importexecs.items():
...
for line in imports:
exec(line)
If one of those .pth import lines calls site.addsitedir(), then addsitedir() calls process_startup_files() again while the outer _exec_imports() is still iterating _pending_importexecs. That causes repeated execution and eventually RuntimeError: dictionary changed size during iteration.
Real-World Trigger
With uv 0.11.7:
uv run --python /Users/jelle/py/cpython/python.exe --with iniconfig python -c 'import iniconfig'
fails before user code runs because uv’s ephemeral overlay .pth contains import site; site.addsitedir(...).
Plain uv run without --with works, so this is specifically triggered by .pth import lines that call site.addsitedir() during site startup.
CPython versions tested on:
3.15
Operating systems tested on:
macOS
Linked PRs
Bug report
Bug description:
I found a regression in Python 3.15’s
site.pystartup-file processing. A.pthfile containing an import line that callssite.addsitedir()can re-enterprocess_startup_files()while_exec_imports()is iterating_pending_importexecs, causing recursive processing of the same.pthfile and eventually a startup failure.This was originally observed with
uv run --with ..., because uv creates an ephemeral overlay.pthfile like:But the issue can be reproduced without uv.
Environment
Minimal Reproducer
Expected Behavior
The
.pthimport line should execute once, add/tmp/site-repro/overlayas a site directory, and the command should print:Actual Behavior
Python repeatedly processes the same
.pthimport line, then fails with errors like:In startup contexts, such as
uv run --with ..., this can become fatal duringsiteinitialization:Likely Cause
In
Lib/site.py,addsitedir()callsprocess_startup_files()unlessdefer_processing_start_files=True.process_startup_files()calls_exec_imports(), which iterates directly over_pending_importexecs.items():If one of those
.pthimport lines callssite.addsitedir(), thenaddsitedir()callsprocess_startup_files()again while the outer_exec_imports()is still iterating_pending_importexecs. That causes repeated execution and eventuallyRuntimeError: dictionary changed size during iteration.Real-World Trigger
With uv 0.11.7:
uv run --python /Users/jelle/py/cpython/python.exe --with iniconfig python -c 'import iniconfig'fails before user code runs because uv’s ephemeral overlay
.pthcontainsimport site; site.addsitedir(...).Plain
uv runwithout--withworks, so this is specifically triggered by.pthimport lines that callsite.addsitedir()during site startup.CPython versions tested on:
3.15
Operating systems tested on:
macOS
Linked PRs