Skip to content

[3.15] Re-entrant calls to site.addsitedir() can fail (affecting uv run) #149504

@JelleZijlstra

Description

@JelleZijlstra

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:

done

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions