Python 3.15 RC1 dropped August 4. Feature freeze is done, the ABI is stable, and the October GA release is now a formality — barring a catastrophic regression. If you maintain a package with C extensions or binary wheels, there is one thing you should do today: publish 3.15 wheels on PyPI. Wheels built against RC1 are ABI-compatible with the final release, and cibuildwheel now builds 3.15 by default without any prerelease flag. The Python team is asking for coverage, not patience. Beyond the wheel story, three features in 3.15 are worth understanding: lazy imports that cut startup by up to 70%, a built-in frozendict after 14 years of community demand, and a production-safe profiler that can sample at 1MHz and attach to a live process without a restart.
Lazy Imports Finally Land in the Language
PEP 810 adds a lazy soft keyword that defers module loading until first use. This is not a new idea — importlib, zipimport, and third-party packages have offered variations for years — but this is the first time it is a language-level feature with a stable API and no import hook gymnastics required.
lazy import json
lazy from pathlib import Path
print("App starting") # neither module is loaded here
data = json.loads(raw_input) # json loads on this line
The performance numbers are not marginal. Meta tested this internally and reported 70% reduction in startup time with 30–40% less memory on cold starts. For serverless functions and CLI tools — where cold start is frequently the only cost that matters — this is substantial. The feature ships with global controls too: -X lazy_imports on the command line, PYTHON_LAZY_IMPORTS=1 as an environment variable, and sys.set_lazy_imports() for runtime configuration.
There are real caveats. Lazy imports only work at module scope — not inside functions, classes, or try/except blocks. Import exceptions surface at first use rather than import time, which shifts where stack traces appear. Security tools that rely on import-time static analysis need to catch up. None of this is a dealbreaker, but it should have shipped five years ago when serverless became the default deployment model for a significant share of Python workloads.
frozendict Ships After 14 Years
PEP 416, the first proposal for a frozen dictionary type, was rejected in 2012. PEP 814 resurrected it with a cleaner design and finally landed it in 3.15. The community has been working around this gap with types.MappingProxyType, third-party frozendict packages, and tuple(sorted(d.items())) patterns. All of those workarounds are now obsolete.
a = frozendict(x=1, y=2)
b = frozendict(y=2, x=1)
a == b # True — comparison ignores insertion order
hash(a) == hash(b) # True — hashable when values are hashable
a["z"] = 3 # TypeError — immutable
The practical wins are straightforward. frozendict can be used as a dictionary key or a set member — something plain dict never could — which makes it the correct type for cache keys, immutable configuration objects, and function arguments that should not be shared across calls. It integrates natively into json, pickle, copy, and pprint. Note that it is not a dict subclass — it inherits directly from object — which means existing code that does isinstance(x, dict) will return False for a frozendict. Check your validation logic.
Tachyon: A Profiler That Actually Works in Production
The new profiling package reorganizes Python’s profiling tools. The old cProfile moves to profiling.tracing. The new addition is profiling.sampling, which ships Tachyon — a statistical sampler that can operate at up to 1,000,000 Hz and attach to a running process by PID.
python -m profiling.sampling attach 12345 # live production process
python -m profiling.sampling run script.py # profile from start
python -m profiling.sampling dump 12345 --async-aware
The production use case matters because cProfile is deterministic and carries 15–20% overhead, making it unsuitable for live systems. Tachyon is statistical and near-zero overhead, which means you can leave it running on production workloads and inspect at will. The standout mode is GIL tracking — it measures exactly how much wall-clock time each thread spends blocked waiting for the Global Interpreter Lock. If you have thread contention you have been unable to prove, this is the tool. Output formats include flame graphs (HTML), collapsed stacks for speedscope, Gecko Profiler format, and a live terminal TUI. Frame pointers are also enabled by default in 3.15 builds (PEP 831), which improves compatibility with external tools like perf and py-spy.
JIT and the Timeline
The JIT compiler is measurably faster — 8–9% geometric mean improvement on x86-64 Linux, 12–13% on macOS AArch64 — and Windows 64-bit now gets the tail-calling interpreter for the first time. It remains experimental. The Steering Council issued a six-month ultimatum in June: produce a proper standards-track PEP or the JIT gets removed. The JIT team is working on it. To benchmark it yourself: python -X jit script.py.
What to do before October: run your test suite against RC1 using python -W error to surface removed APIs. If you build C extension wheels, publish 3.15 builds to PyPI now — the RC1 announcement has the full checklist. And try python -X lazy_imports myapp.py — even without code changes, global lazy imports may cut your startup time visibly. The second release candidate lands September 1. The final release follows in October.













