Python 3.15 Slips a Week as Lazy Imports Hold Up the Final Release
A surprise third release candidate pushes 3.15.0 to 9 October, and points at the feature most likely to surprise teams after upgrading.
Python's release managers published Python 3.15.0rc3 on 2 October instead of the final release that had been planned for that week. The reason given was last-minute release blockers in lazy imports, the headline feature of 3.15. The team chose to include the fixes in 3.15.0 rather than ship without them, and wanted time to test them, so the final release moves to 9 October.
The candidate contains around 156 bug fixes, build improvements and documentation changes from 82 contributors since rc2. The ABI is frozen, so binary wheels built against any 3.15 release candidate will work with the final release. A one-week slip is minor. What it tells us about the feature is more useful.
What lazy imports change
PEP 810 adds a lazy soft keyword. Writing lazy import json or lazy from pathlib import Path creates a lightweight proxy, and the real module is loaded the first time the name is used. Imports can stay at the top of the file, where they are readable, while startup only pays for modules a given run actually touches.
For CLIs, serverless functions and anything that imports a heavy scientific or ML stack only to print a help message, this is the biggest startup-time improvement Python has offered in years, and it replaces the workaround of moving imports inside functions.
The behaviour change is in when errors appear. If a lazily imported module fails to load, the exception is raised at first use, not at the import line. The traceback includes both locations, which helps, but code that relied on an ImportError at the top of a module to choose a fallback will behave differently if that import is made lazy.
There is also a global switch. The -X lazy_imports option and the PYTHON_LAZY_IMPORTS environment variable accept all, which makes every import lazy by default, or normal, the default, which only respects the keyword. A filter function set through sys.set_lazy_imports_filter() can restrict laziness to your own packages.
I would be cautious with all. Plenty of Python code depends on side effects at import time: plugin registries, ORM model registration, monkey-patching, logging configuration. Making those imports lazy means the side effect happens later, or never. That last-minute blockers appeared here is not surprising; it is a hard feature to get right across the whole import system. Treat global lazy mode as something to measure carefully, and use the filter to keep third-party dependencies eager.
The rest of the release
- UTF-8 by default. Under PEP 686, Python now uses UTF-8 as the default encoding regardless of locale. This mostly affects Windows, where code that opened files without an explicit encoding previously used the system code page.
PYTHONUTF8=0restores the old behaviour. - New built-ins.
frozendictandsentineljoin the language, and comprehensions support unpacking. - A faster experimental JIT. The release notes cite a 7–8% geometric mean improvement on x86-64 Linux over the standard interpreter and 11–12% on Apple Silicon macOS over the tail-calling interpreter. These are averages, and still experimental, so benchmark your own workload before you enable it.
- Better profiling. Tachyon, a high-frequency sampling profiler, arrives in a new
profilingpackage, and frame pointers are now enabled by default, which makes system-level profilers far more useful. - Packaging changes. Official Windows 64-bit binaries now use the tail-calling interpreter, and the macOS installer includes free-threading support by default.
One more warning from the release page: on macOS 27, tkinter applications, including IDLE, can hang when opening dialogs, because of an operating-system behaviour change that affects current versions of Tk. If you depend on Tk-based tools on the Mac, check the linked issue before upgrading.
What to do this week
For library maintainers, the release team's request is the usual one: build and publish 3.15 wheels now, so that users can upgrade on day one. For application teams, the plan is simple. Run your test suite on rc3, try lazy on your heaviest imports, measure startup, and keep the global switch off until you have good reason to turn it on.
Sources