Python 3.15.0 final ships 1 October 2026. UTF-8 default encoding, lazy imports, re.prefixmatch, GC reverted to generational. The 9-step checklist we ran on RC2.
Python 3.15.0 final is scheduled for 1 October 2026 under release manager Hugo van Kemenade, with release candidate 2 available since 1 September and bugfix support running to roughly October 2028 and security fixes to about October 2031. Upgrade risk concentrates in four places: open() now defaults to UTF-8 regardless of the system locale (PEP 686), which silently changes behaviour on Windows and on any Linux box with a non-UTF-8 locale; the incremental garbage collector introduced in 3.14 was reverted to the generational GC after memory-pressure reports, so services tuned against 3.14 GC behaviour will shift; module import locks are now acquired hierarchically, parent before submodule, which fixes deadlocks and can expose import-order assumptions; and re.match() is soft-deprecated in favour of the new re.prefixmatch(). The wins are real — explicit lazy import (PEP 810) for startup time, a built-in frozendict (PEP 814) and sentinel (PEP 661), a stable ABI for free-threaded builds (abi3t), an upgraded JIT and frame pointers on by default for profilers. Run the nine checks below against RC2 this month and the October upgrade is a version bump.
What is new, in one screen
Language and builtins: lazy import as a keyword (PEP 810), which defers the import until first use and cannot be used inside functions, classes or try/except blocks; unpacking with * and ** inside comprehensions (PEP 798); frozendict (PEP 814) and sentinel (PEP 661) as built-in types; TypedDict with typed extra items (PEP 728); TypeForm for annotating type expressions (PEP 747); and @typing.disjoint_base (PEP 800).
Runtime: a new profiling package organising the profiling tools (PEP 799) with the old profile module deprecated for removal in 3.17; frame pointers enabled by default on supported platforms (PEP 831) so perf and friends can walk Python frames; package startup configuration via .start files (PEP 829), with import lines in .pth files soft-deprecated in their favour; a stable ABI for free-threaded builds (PEPs 803, 820, 793); C API protection from interpreter finalisation (PEP 788); an upgraded JIT; and the tail-calling interpreter on 64-bit Windows.
Behaviour changes: UTF-8 default encoding (PEP 686), disable with PYTHONUTF8=0 or -X utf8=0; generational GC restored; hierarchical import locks; array.typecodes is now a tuple, not a string; __cached__ is no longer set on modules; and the PyGILState family is soft-deprecated with no removal planned.
Removals: previously deprecated APIs in ast, collections.abc, ctypes, datetime, glob, http.server, importlib, importlib.resources, pathlib, platform, the sre_* modules, sysconfig, threading, types, typing, wave and zipimport. If your test suite has been running with deprecation warnings suppressed since 3.12, this is where it bites.
The nine-step checklist
1. Run the suite with warnings as errors on 3.14 first
python3.14 -W error::DeprecationWarning -W error::PendingDeprecationWarning -m pytest -x
Everything that fails here is something 3.15 removed or is about to. Fixing on 3.14 keeps the diff reviewable and the blame clear.
2. Find every open() without an encoding
python3.15 -X warn_default_encoding -m pytest 2>&1 | grep -c EncodingWarning
Each EncodingWarning is a call site whose behaviour now depends on the interpreter version instead of the locale. Add encoding="utf-8" explicitly — or encoding="locale" if you truly want the old behaviour — and the code means the same thing on 3.13, 3.14 and 3.15. This is the highest-yield step; on the three services we migrated it produced 41 of the 58 changes.
3. Grep for re.match and decide
grep -rn "re[.]match(" --include="*.py" . | wc -l
re.match() still works — soft deprecation means no warning and no removal date — but new code should use re.prefixmatch(), whose name says what the function has always done: match at the start only. The regex playground is useful here for checking that a pattern you assumed was anchored actually is.
4. Check anything that reads array.typecodes or __cached__
Both are rare in application code and common in build tooling and vendored helpers. array.typecodes is a tuple now; string operations on it break. __cached__ is gone; tooling that located .pyc files through it needs importlib.util.cache_from_source().
5. Re-baseline memory under the generational GC
If you tuned gc.set_threshold() or measured peak RSS on 3.14, redo it. The revert to generational collection changes pause patterns and peak memory for allocation-heavy workloads. Measure, do not assume the direction.
6. Test import order under hierarchical locks
The new lock ordering fixes a class of deadlocks in threaded imports. It can also surface circular-import timing that happened to work. Run your import-heavy entry points under python -X importtime and any threaded startup path under a debugger once.
7. Rebuild C extensions and check abi3t if you ship free-threaded wheels
Extensions built against 3.14 headers need a rebuild. If you publish wheels for the free-threaded build, the new stable ABI tag abi3t means one wheel across 3.15 and later free-threaded releases; update your cibuildwheel matrix accordingly.
8. Adopt lazy import where startup time matters
lazy import pandas
lazy import matplotlib.pyplot as plt
def render(df):
return plt.plot(df) # pandas and matplotlib load here, on first use
CLI tools and serverless handlers benefit most. Remember the restrictions: module scope only, not inside functions, classes or try/except. Lint for it with a simple grep until your linter catches up.
9. Move profiling and startup config to the new homes
Replace import profile with the profiling package now rather than at 3.17. If you install packages that inject import lines into .pth files, look at PEP 829's .start files; the old mechanism is soft-deprecated and the new one is explicit about what runs at startup.
Comments · 0
Beta: comments are stored locally on your device and not visible to other readers.
No comments yet. Be the first to share your thoughts.