Skip to content

gh-146369: Ensure PYTHON_LAZY_IMPORTS=none overrides __lazy_modules__#146371

Merged
pablogsal merged 3 commits intopython:mainfrom
hugovk:3.15-fix-lazy-dunder-none
Mar 25, 2026
Merged

gh-146369: Ensure PYTHON_LAZY_IMPORTS=none overrides __lazy_modules__#146371
pablogsal merged 3 commits intopython:mainfrom
hugovk:3.15-fix-lazy-dunder-none

Conversation

@hugovk
Copy link
Member

@hugovk hugovk commented Mar 24, 2026

When a regular import is in __lazy_modules__, _PyEval_LazyImportName is called with lazy = 0, because this is a regular import with no lazy keyword.

The switch statement also sets it to zero, because we've set either PYTHON_LAZY_IMPORTS=none or -X lazy_imports=none.

However, the first if statement checks !lazy (true) and then checks if the import is in __lazy_modules__. Well, it is, so we treat it as lazy.

Instead, we should skip this if statement if we've set either PYTHON_LAZY_IMPORTS=none or -X lazy_imports=none. These should override regular imports using the backwards compatibility dunder, just like they override the lazy keyword.

With the fix, the override still works for eager:

hyperfine --warmup 10 --runs 20 \
   "./python.exe lazy-eager.py" \
   "PYTHON_LAZY_IMPORTS=normal ./python.exe lazy-eager.py" \
   "PYTHON_LAZY_IMPORTS=none   ./python.exe lazy-eager.py" \
   "PYTHON_LAZY_IMPORTS=all    ./python.exe lazy-eager.py" \
   "./python.exe -X lazy_imports=normal lazy-eager.py" \
   "./python.exe -X lazy_imports=none   lazy-eager.py" \
   "./python.exe -X lazy_imports=all    lazy-eager.py"
Benchmark 1: ./python.exe lazy-eager.py
  Time (mean ± σ):      16.8 ms ±   0.4 ms    [User: 13.2 ms, System: 2.9 ms]
  Range (min … max):    16.0 ms …  17.4 ms    20 runs

Benchmark 2: PYTHON_LAZY_IMPORTS=normal ./python.exe lazy-eager.py
  Time (mean ± σ):      16.9 ms ±   0.6 ms    [User: 13.2 ms, System: 3.0 ms]
  Range (min … max):    15.6 ms …  17.8 ms    20 runs

Benchmark 3: PYTHON_LAZY_IMPORTS=none   ./python.exe lazy-eager.py
  Time (mean ± σ):     137.4 ms ±   4.2 ms    [User: 120.4 ms, System: 15.2 ms]
  Range (min … max):   133.9 ms … 153.4 ms    20 runs

Benchmark 4: PYTHON_LAZY_IMPORTS=all    ./python.exe lazy-eager.py
  Time (mean ± σ):      17.1 ms ±   0.6 ms    [User: 13.4 ms, System: 3.0 ms]
  Range (min … max):    16.0 ms …  18.1 ms    20 runs

Benchmark 5: ./python.exe -X lazy_imports=normal lazy-eager.py
  Time (mean ± σ):      16.7 ms ±   0.4 ms    [User: 13.2 ms, System: 2.9 ms]
  Range (min … max):    15.9 ms …  17.6 ms    20 runs

Benchmark 6: ./python.exe -X lazy_imports=none   lazy-eager.py
  Time (mean ± σ):     139.1 ms ±   3.9 ms    [User: 121.9 ms, System: 15.2 ms]
  Range (min … max):   134.9 ms … 153.4 ms    20 runs

Benchmark 7: ./python.exe -X lazy_imports=all    lazy-eager.py
  Time (mean ± σ):      17.0 ms ±   0.5 ms    [User: 13.3 ms, System: 3.0 ms]
  Range (min … max):    16.0 ms …  18.3 ms    20 runs

Summary
  ./python.exe -X lazy_imports=normal lazy-eager.py ran
    1.01 ± 0.03 times faster than ./python.exe lazy-eager.py
    1.01 ± 0.05 times faster than PYTHON_LAZY_IMPORTS=normal ./python.exe lazy-eager.py
    1.02 ± 0.04 times faster than ./python.exe -X lazy_imports=all    lazy-eager.py
    1.02 ± 0.05 times faster than PYTHON_LAZY_IMPORTS=all    ./python.exe lazy-eager.py
    8.23 ± 0.33 times faster than PYTHON_LAZY_IMPORTS=none   ./python.exe lazy-eager.py
    8.33 ± 0.31 times faster than ./python.exe -X lazy_imports=none   lazy-eager.py

And now also for the dunder (compare the issue):

hyperfine --warmup 10 --runs 20 \
   "./python.exe lazy-dunder.py" \
   "PYTHON_LAZY_IMPORTS=normal ./python.exe lazy-dunder.py" \
   "PYTHON_LAZY_IMPORTS=none   ./python.exe lazy-dunder.py" \
   "PYTHON_LAZY_IMPORTS=all    ./python.exe lazy-dunder.py" \
   "./python.exe -X lazy_imports=normal lazy-dunder.py" \
   "./python.exe -X lazy_imports=none   lazy-dunder.py" \
   "./python.exe -X lazy_imports=all    lazy-dunder.py"
Benchmark 1: ./python.exe lazy-dunder.py
  Time (mean ± σ):      17.4 ms ±   0.5 ms    [User: 13.6 ms, System: 3.0 ms]
  Range (min … max):    16.4 ms …  18.3 ms    20 runs

Benchmark 2: PYTHON_LAZY_IMPORTS=normal ./python.exe lazy-dunder.py
  Time (mean ± σ):      18.2 ms ±   2.1 ms    [User: 13.9 ms, System: 3.1 ms]
  Range (min … max):    16.6 ms …  25.7 ms    20 runs

  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

Benchmark 3: PYTHON_LAZY_IMPORTS=none   ./python.exe lazy-dunder.py
  Time (mean ± σ):     138.9 ms ±   1.8 ms    [User: 121.8 ms, System: 15.4 ms]
  Range (min … max):   134.9 ms … 141.6 ms    20 runs

Benchmark 4: PYTHON_LAZY_IMPORTS=all    ./python.exe lazy-dunder.py
  Time (mean ± σ):      17.7 ms ±   0.5 ms    [User: 13.7 ms, System: 3.1 ms]
  Range (min … max):    16.9 ms …  18.7 ms    20 runs

Benchmark 5: ./python.exe -X lazy_imports=normal lazy-dunder.py
  Time (mean ± σ):      17.3 ms ±   0.5 ms    [User: 13.5 ms, System: 3.0 ms]
  Range (min … max):    16.5 ms …  18.3 ms    20 runs

Benchmark 6: ./python.exe -X lazy_imports=none   lazy-dunder.py
  Time (mean ± σ):     138.6 ms ±   4.4 ms    [User: 121.1 ms, System: 15.5 ms]
  Range (min … max):   130.6 ms … 154.4 ms    20 runs

Benchmark 7: ./python.exe -X lazy_imports=all    lazy-dunder.py
  Time (mean ± σ):      18.1 ms ±   0.7 ms    [User: 14.0 ms, System: 3.1 ms]
  Range (min … max):    17.0 ms …  19.1 ms    20 runs

Summary
  ./python.exe -X lazy_imports=normal lazy-dunder.py ran
    1.01 ± 0.04 times faster than ./python.exe lazy-dunder.py
    1.02 ± 0.04 times faster than PYTHON_LAZY_IMPORTS=all    ./python.exe lazy-dunder.py
    1.04 ± 0.05 times faster than ./python.exe -X lazy_imports=all    lazy-dunder.py
    1.05 ± 0.12 times faster than PYTHON_LAZY_IMPORTS=normal ./python.exe lazy-dunder.py
    8.00 ± 0.34 times faster than ./python.exe -X lazy_imports=none   lazy-dunder.py
    8.02 ± 0.26 times faster than PYTHON_LAZY_IMPORTS=none   ./python.exe lazy-dunder.py

Copy link
Member

@pablogsal pablogsal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pablogsal
Copy link
Member

Good catch @hugovk!

@pablogsal pablogsal merged commit d0e66ef into python:main Mar 25, 2026
64 checks passed
@hugovk hugovk deleted the 3.15-fix-lazy-dunder-none branch March 25, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants