Python 3.15 is scheduled for final release on October 9 — two days out. A third release candidate dropped on October 2, which was not in the original plan. A late-breaking bug in the lazy imports machinery forced it. The fix landed, the schedule held, and RC3 shipped with 156 bugfixes from 82 contributors. Here is what actually changed, what could break existing code, and why this Python 3.15 release matters more than the version bump implies.
Python 3.15 Lazy Imports: Faster Startup Without Code Changes
PEP 810 introduces a lazy keyword that defers module loading until first use. Instead of paying the import cost at startup, you pay it when the code path that actually needs the module runs.
lazy import json
lazy from pathlib import Path
# json and pathlib are not loaded yet
data = json.loads('{"key": "value"}') # json loads here, on first use
For CLI tools and large applications with optional features, this is significant. A tool that imports 15 modules at startup — even if only three of them run for a given command — now loads only those three. You can also enable lazy imports globally without touching code: set PYTHON_LAZY_IMPORTS=1 or pass -X lazy_imports.
However, there is a real catch. Lazy imports defer side effects too. Any module that registers itself on import — via @register decorators, logging setup, or plugin discovery — will silently fail to register if nothing touches the module name. This is not theoretical; it is why RC3 happened. Edge cases in the lazy import machinery kept surfacing under real-world test conditions. If your codebase relies on import-time side effects, audit those paths before enabling this feature globally.
Tachyon Profiler: Profile Live Python Processes Without Restarting
cProfile has been in the stdlib for years and is genuinely useful. It is also a tracing profiler that slows your application 2-3x. The standard workaround has been third-party tools like py-spy — fast, but not in the stdlib, requiring separate installation and elevated permissions in production environments.
Tachyon (profiling.sampling) changes that. It is a statistical sampling profiler that reads a running process’s stack from outside, at up to 1,000,000 Hz, with near-zero overhead in default mode. At the recommended production rate of 100-1000 Hz with the --blocking flag, overhead is 1-2%. You can attach to a live server by PID, collect a flamegraph, and detach — without the application ever restarting.
python -m profiling.sampling attach 12345 --flamegraph bottleneck.html
python -m profiling.sampling run --mode cpu my_script.py
Tachyon is async-aware, reconstructing asyncio task stacks. It outputs HTML flamegraphs, Firefox Profiler format, heatmaps, and a live TUI. This is not an incremental improvement on cProfile — it is a different category of tool, now shipping in the standard library. It was presented at PyCon US 2026 and is directly integrated into 3.15.
Related: Node.js 26 Is Now LTS: Temporal API, 5 Breaking Changes, and Your Upgrade Checklist
frozendict and the UTF-8 Default: One Welcome, One Risky
PEP 814 adds frozendict as a built-in. It is immutable, hashable, and works as a dictionary key or set member. The hash is order-independent, so frozendict(a=1, b=2) and frozendict(b=2, a=1) are equal and produce the same hash. It integrates natively with json, pickle, and copy. Everyone who has built a frozen config object or reached for types.MappingProxyType gets to delete that code.
The UTF-8 default (PEP 686) is the more disruptive change. open('file.txt') without an explicit encoding= now defaults to UTF-8 everywhere, regardless of the system locale. On Windows systems using code pages like cp1252, code that previously read locale-encoded files correctly will now attempt to read them as UTF-8 and either produce garbled output or raise a decode error. The fix is straightforward — always specify encoding= — but every call site needs to be found first. Search your codebase for open( calls without an encoding argument before upgrading.
The JIT Numbers Are Real, Just Not Revolutionary
Python’s experimental JIT compiler reports 7-8% geometric mean speedup on x86-64 Linux and 11-12% on AArch64 macOS. On ETL pipelines and long-running pure-Python scripts, gains can reach 5-40%. The JIT is still opt-in, enabled via the --jit flag when building CPython. For I/O-bound work or short-lived scripts, the improvement is minimal.
This is not PyPy — those numbers are still 3-5x. Nevertheless, a 10-40% free speedup on compute-heavy workloads requiring no code changes is worth testing on your specific use cases. The 3.15 JIT handles more bytecode operations and control flow patterns than 3.14, so workloads that saw no benefit before may now benefit. Check the What’s New in Python 3.15 documentation for full details on JIT improvements.
Before You Upgrade to Python 3.15
Three things to check before rolling out Python 3.15 in production. First, search for all open( calls that lack an encoding= argument — the UTF-8 default is the most likely source of silent data issues. Second, if you use any import-time side effects (registration patterns, plugin loading, logging configuration triggered by imports), test lazy imports carefully before enabling the flag globally. Third, if you ship C extensions or depend on them, check whether wheels are available for 3.15.
One confirmed issue on macOS 27.0: IDLE and other Tkinter-based applications hang when opening certain dialog menus (About, Settings, Open Module). This is a known macOS 27.0 + Tk incompatibility tracked in the CPython issue tracker. If you rely on IDLE or ship Tkinter GUI applications, defer the macOS 27.0 upgrade until a Tk patch is available.
Key Takeaways
- Python 3.15 final releases October 9. RC3’s surprise appearance was caused by lazy import edge cases — the feature is complex enough to warrant caution before enabling globally.
- Lazy imports (
lazy import x) are the headline feature: faster startup with no code changes for most apps, but import-time side effects are deferred and can break silently. - Tachyon profiler is genuinely new capability — attach to a live process by PID, collect flamegraphs, zero restart required. This replaces a real gap in the stdlib.
- The UTF-8 encoding default is correct long-term but will bite legacy codebases on Windows. Audit
open()calls before upgrading. - JIT performance gains are real (7-12% geometric mean, up to 40% on pure-Python workloads) but opt-in and workload-dependent — benchmark before assuming benefits.













