Skip to content

Sample code for: Python 3.15 Preview: Lazy Imports - #808

Merged
bzaczynski merged 3 commits into
masterfrom
python315-lazy-imports
Sep 6, 2026
Merged

Sample code for: Python 3.15 Preview: Lazy Imports#808
bzaczynski merged 3 commits into
masterfrom
python315-lazy-imports

Conversation

@stephengruppetta

Copy link
Copy Markdown
Contributor

Sample code for the Real Python tutorial Python 3.15 Preview: Lazy Imports.

Standard library only, but it needs Python 3.15 — most files use the lazy keyword from PEP 810, which is a SyntaxError on anything earlier. Every file here was run against a CPython 3.15.0rc1 build, and the tutorial quotes its real output.

Layout

Path Section
noisy_module.py, probe.py Defer a Whole Module
shapes.py, partial.py Defer a Name From a Module
badfunc.py Find Out Where lazy Isn't Allowed
report_cli/ Speed Up a Real CLI
type_checking_guard.py, lazy_annotation.py Retire the if TYPE_CHECKING Dance
fail.py Read a Deferred Import Error
circular/ Don't Expect a Circular Import Fix
bridge.py, allmode.py Go Lazy Without the Keyword

report_cli/ holds three versions of the same CLI: cli_eager.py, cli_lazy.py (five lazy keywords, nothing else changed), and cli_too_lazy.py, which also defers the two plugin imports and so silently empties the format registry. bench.py times any of them, and a --load-all flag reads every deferred name so you can check that deferral costs nothing once the modules are used.

circular/ has four self-contained folders: eager/ fails, lazy/ is fixed by deferring one side, and init_eager/ + init_lazy/ both fail with the same ImportError because the cycle needs a value during module initialization.

One change outside the new folder

pyproject.toml adds python315-lazy-imports to ruff's exclude list. Two reasons:

  1. ruff 0.14.1 can't parse the lazy keyword. ruff format --check fails with invalid-syntax: Simple statements must be separated by newlines or semicolons on any file using it, which would break CI.
  2. With the folder excluded, the files can follow the Real Python style guide's blank-line rules — one blank line between definitions, not PEP 8's two — so the snippets in the tutorial and the files in this folder stay byte-identical.

CI passes locally with the exclusion in place: ruff format --check, ruff check, and dircheck.py are all green.

Stephen Gruppetta and others added 3 commits August 21, 2026 10:18
bench.py ignored the subprocess return code, so a script that crashed
was timed as if it had succeeded. A reader whose python isn't 3.15
would have seen cli_lazy.py die with a SyntaxError and bench.py report
it as a 2.5x speedup:

    $ python3.14 bench.py cli_lazy.py --help
    cli_lazy.py: 50 ms (best of 10)

That's the same silent, plausible-looking wrongness the tutorial warns
about in its own side-effects section. bench.py now checks the return
code, surfaces the captured stderr, and refuses to run at all below
3.15.

Also note in the README which files raise on purpose, and that a pyenv
build needs Tk headers or cli_eager.py won't start.
Resolve the pyproject.toml conflict by keeping both ruff excludes:
master's ai-benchmark and this branch's python315-lazy-imports.

Also drop uv.lock. Master has no lockfile and pyproject.toml declares
no [project] table, so the 3-line file was an incidental artifact of
running uv inside the repo, unrelated to the tutorial's sample code.

@bzaczynski bzaczynski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verified during Final QA for the tutorial (CMS post 2288).

Merge conflict resolved. The branch was 10 commits behind and conflicted in pyproject.toml, where master had added ai-benchmark to [tool.ruff].exclude. Merged master in and kept both entries, so the diff against master is now a single clean hunk.

Dropped uv.lock. Master has no lockfile and pyproject.toml declares no [project] table, so the 3-line file was an incidental artifact of running uv in the repo rather than anything the tutorial needs.

Kept the ruff exclusion. Still justified: master pins ruff==0.14.1, which can't parse the lazy keyword, and the exclusion is what lets these files keep the style guide's one-blank-line rule so they stay byte-identical to the article's code blocks.

Code verified on CPython 3.15.0b3. Every file runs as the tutorial shows:

  • probe.py, partial.py, shapes.py, bridge.py, allmode.py, lazy_annotation.py — output matches the article exactly.
  • The deliberate failures all raise the documented exception: badfunc.py (SyntaxError: lazy import not allowed inside functions), fail.py (chained ImportErrorModuleNotFoundError), type_checking_guard.py (NameError in the __annotate__ frame), and circular/eager, circular/init_eager, circular/init_lazy.
  • circular/lazy/ resolves the cycle, and deferring either side works in either import order.
  • report_cli/: 145 ms eager vs 58 ms lazy on --help (220 → 73 modules), and 128 vs 126 ms under --load-all, confirming deferral costs nothing once the modules are used. cli_too_lazy.py --list-formats prints the empty registry as intended.
  • bench.py's return-code guard works — it refuses to run on 3.14 instead of timing a crashed process.

Repo gates pass locally with the pinned ruff: ruff format --check, ruff check, and .github/workflows/dircheck.py are all green, as is Linux314 on this head.

Article code blocks were diffed against these files: the 14 full-file blocks are byte-identical, and the three linenums="8" excerpts match their files exactly, with hl_lines="4-8" landing on the five lazy lines.

@bzaczynski
bzaczynski merged commit de6f46e into master Sep 6, 2026
1 check passed
@bzaczynski
bzaczynski deleted the python315-lazy-imports branch September 6, 2026 14:46
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.

2 participants