source-explainer

Python 3.15 gives maintainers a reason to raise their floor, and a warning about lazy imports

Python 3.10 support ended on October 1, so projects can raise their floor to 3.11. Python 3.15’s lazy imports can trim CLI startup, provided plugin side effects are tested.

Python 3.10 reached end of life on October 1, 2026. Python 3.15.0 followed on October 9. Those dates matter separately: maintainers can now stop testing and shipping compatibility code for 3.10, while 3.15 remains a feature target that many projects cannot require yet. CPython’s supported versions schedule puts Python 3.11 in security-fix support through October 2027, so a library that supports every maintained Python can raise its minimum version to 3.11 without making 3.15 its baseline.

That is the practical change Simon Willison described when he said goodbye to 3.10. His first Datasette cleanup after the cutoff removed a SQLite-authorizer workaround, updated the project’s classifiers and test matrix to Python 3.11 through 3.15, and ran its suite on Python 3.11.15 and 3.15.0 before merge. The Datasette pull request is a useful model of the work behind a version-floor increase: delete old branches, remove a CI lane, update packaging metadata, then test the remaining versions. New syntax can wait until the user base permits it.

For projects that can deploy 3.15, explicit lazy imports are the release feature with the clearest operational use. A program can write `lazy import json` or `lazy from pathlib import Path`; Python defers locating, loading, compiling and executing that module until code first uses it. The Python 3.15 documentation identifies command help and multi-command CLIs as intended cases, where loading optional commands and their dependencies can cost seconds before useful work begins.

That maps closely to plugin-based command-line software. Willison’s LLM tool has advised plugin authors to put expensive imports inside functions, since an installed plugin using a package such as `sentence_transformers` could slow every invocation. Lazy-import syntax makes that intent visible at module scope. It can keep `tool --help` and lightweight subcommands from paying for a model stack they never touch.

The tradeoff is timing. PEP 810 says an import failure appears when the name is first used, and module-level registration, monkey-patching and initialization likewise happen later. Plugin registries are an especially sharp edge: a plugin imported lazily has not executed its registration code. Teams adopting the feature need an explicit discovery or initialization step where registration must occur, plus tests for first-use errors and import ordering. The PEP also advises installers that import from their own environment to resolve imports before installing packages, preventing a newly installed package from shadowing a dependency waiting to load.

The release has other tools worth separating from broad performance claims. Python 3.15 adds `profiling.sampling`, which can attach to a running process by PID and produce profiles including flamegraphs and collapsed stacks. The documentation describes it as “virtually zero overhead,” a useful property for investigating a live service without a restart. Sampling still reports statistical observations, so it answers where time appears to go, not an exact count of every function call. That makes it a good first measurement before deciding whether delayed imports are worth the changed initialization behavior.

stagirus