Real Python wrote up a surprise third release candidate on October 5. PEP 790 had put 3.15.0 on October 1. Release manager Hugo van Kemenade said last-minute lazy-import blockers made a third candidate the honest move. 3.15.0rc3 went out October 2 with about 156 fixes from 82 contributors since rc2. The final was retargeted to Friday, October 9. docs.python.org’s 3.15 “What’s new” page is titled as 3.15.0 documentation and was last-modified October 10. The ABI did not thaw. If you sat out October because rc2 was supposed to be the last candidate, that delay is over.
We already filed rc2 as the last candidate, lazy imports against FastAPI startup, and rc1 freezing the ABI. This piece is the calendar slipping and then stopping. Do not reread the PEP list unless you skipped those.
What slipped, and what did not
Van Kemenade’s reason, as Real Python quoted it: lazy-import release blockers, and it made sense to include the fixes in 3.15.0 final. That is PEP 810 taking a bite out of the ship date, not a new feature sneaking in. Real Python is explicit: the feature set has been frozen since May. The ABI has been frozen since August and stayed frozen through rc3. Between rc3 and the final, only reviewed bug fixes were supposed to land.
Python Bytes #499, October 6, repeats the counts: about 156 fixes, 82 contributors, final on October 9. Headline features remain explicit lazy imports (PEP 810), frozendict (PEP 814), sentinel (PEP 661), and UTF-8 as the default encoding (PEP 686). No more ABI changes, so library authors should be building 3.15 wheels now. uv 0.12.23 already knew about 3.15.0rc3.
Hacker News titled a thread “Python 3.15.0” on October 9. Comments treat the final as something you can run. simonw posted uvx --python 3.15 whatsnewt. Other comments quote What’s New on the experimental JIT: 7 to 8 percent geometric mean on x86-64 Linux over the standard interpreter, 11 to 12 percent on AArch64 macOS over the tail-calling interpreter. Those are What’s New numbers, not a benchmark you should paste into a board deck without reading the caveats on that page.
Python Weekly issue 766, October 8, pointed at Miguel Grinberg’s 3.15 benchmarks: noticeable gains, not a leap, workload-dependent. That is the adult version of the JIT bullet. If your workload is not the one that moved, you will not feel 8 percent.
The docs host updating 3.15 What’s New on October 10 is the operational signal this blog is using. python.org’s marketing download surface can lag or route through the install manager. CI should pin the version it actually fetched, not a homepage screenshot.
What you do Monday if you ship wheels
Build 3.15 wheels. The ABI freeze was the green light in August. rc3 was extra testing for lazy import, not a new ABI. If your extension compiled on rc1, it should compile now. If it does not, that is your bug, not a reason to wait for 3.15.1 before you have even tried.
Test lazy import the way your app actually starts. We already said a CLI and a FastAPI app are different animals. A surprise rc3 exists because the feature had blockers late. Your codebase may have the same class of bug: an import that looks side-effect free and is not. If you enable explicit lazy imports, watch first-call latency, not only process start. Start time can look pretty while the first request pays the import.
Do not treat frozendict and sentinel as reasons to rewrite dictionaries this week. They are language features with PEPs. They are not a migration. UTF-8 default (PEP 686) is the one that bites Windows tools that still thought they had a locale escape hatch. If your test matrix includes Windows, run it on 3.15 before you announce support.
uv users: 0.12.23 had rc3. Upgrade uv before you assume uv python install 3.15 is the final. Then pin the exact CPython you installed in the lockfile you already use. “3.15” as a floating selector is how you get a surprise on a Friday.
If you cannot ship a wheel this week, publish a tested-on line in the README: 3.15.0, date, platform. Silence reads as “we did not try.”
The security train that did not wait for 3.15
Python Bytes notes that on October 1, while 3.15 was slipping, CPython shipped 3.10.22, 3.11.17, 3.12.15, 3.13.16, and 3.14.8 with nine common security fixes covering SSL validation, tarfile and zipfile handling, and urllib credentials. That is the boring upgrade you can do without a PEP 810 conversation.
If production is on 3.12 or 3.13, apply those patches this week even if 3.15 is still a sidecar. A lazy-import final does not patch a tarfile hole on the interpreter you actually run.
Python Weekly also logged Django security releases 6.1.2, 6.0.9, and 5.2.18. We already dedicated a post to 6.1.2. Mentioned here only so web people do not skip the framework while they argue about frozendict.
There is a longer CPython-plus-Rust conversation in that same weekly: optional Rust zlib in 3.16 as a possible start. That is 3.16. It is not your Monday wheel. Do not hold 3.15 support hostage to a zlib experiment.
What not to announce
Do not announce “we are waiting for the real 3.15” if What’s New already says 3.15.0. Waiting was rational between October 1 and rc3. It is theater now.
Do not announce a company-wide lazy-import mandate. PEP 810 is explicit lazy imports. Explicit means you opt in at the import site. A mandate that rewrites every import in a 400-package monorepo is how you discover the next blocker in production instead of in rc3.
Do not put 7 to 8 percent JIT on a slide next to a latency SLO. Grinberg’s spread is the rebuttal. Measure your app or do not quote the number.
Do not drop 3.10 the day you add 3.15. The October 1 security releases still treat 3.10 as alive. Your support matrix is a contract with users, not a vibe about new PEPs.
Do not skip Windows. PEP 686 is why.
Security releases you can ship without a PEP argument
October 1 was not empty just because 3.15 missed its date. 3.10.22 through 3.14.8 carried the same nine fixes. SSL validation, tarfile, zipfile, urllib credentials. If your fleet is 3.12, that is the release you install this week. If your fleet is 3.14, same. 3.15.0 does not patch a machine you have not upgraded.
Put the security tags in the same PR as the 3.15 CI line only if both jobs are green. Do not block a 3.13.16 roll because a 3.15 wheel failed on an optional extra. Those are different risk registers. A tarfile fix is a CVE conversation. A lazy-import failure in an extra is a compatibility conversation.
Django’s 6.1.2 / 6.0.9 / 5.2.18 set, logged in the same Python Weekly issue, is still the web-framework half of the week. If you run Django, that patch is independent of whether your pyproject.toml classifiers mention 3.15 yet. Classifier vanity is not a CVE.
Rust-in-CPython, also in that weekly as an optional zlib in 3.16, is a mailing-list object. You can read it. You cannot schedule it. Anyone using it as a reason to skip 3.15 wheels is looking for a delay, not a dependency.
A reasonable matrix for the rest of October
Keep 3.12 and 3.13 as the production defaults until your test suite has a week of 3.15 nightly without import surprises. Add 3.15 to CI as allowed-to-fail for 48 hours if you must, then make it required for libraries that claim 3.15 support.
For applications: run staging on 3.15 with lazy imports off, then enable them on the hot import path you already profiled in September. If you never profiled, you are not late to a feature. You are late to a profile.
For libraries: wheels, then a tox/pytest line, then a changelog note that names 3.15.0, not “3.15 rc.” Users copy changelog language into pin files.
Packaging news in the same week, and what it is not
Real Python’s October roundup did not stop at rc3. It also noted that the first Python Packaging Council had been elected and immediately handed a PEP written by one of its own members, and that PyPI changed how it counts downloads. If your package’s download chart fell off a cliff this month, read that counting change before you write a post-mortem. A chart is not a user revolt until you know what the y-axis became.
None of that is a reason to delay 3.15 wheels. Council PEPs are a 2026 process story. Your cdn and your cibuildwheel job are a this-week story. Do not merge the two in a stand-up.
Verify the interpreter you installed. python -V should say 3.15.0, not 3.15.0rc3. If it still says rc3, your uv cache or pyenv alias is stale. uv python install 3.15 after upgrading uv is the path Real Python already sketched for the candidate; run it again for the final. simonw’s uvx --python 3.15 one-liner is a smoke test, not a release process. Smoke tests are for lunch. Wheels are for the job.
If you maintain a manylinux wheel, run the 3.15 job on the same image you use for 3.14. An ABI freeze means the C API you compiled against in August still holds. It does not mean a new manylinux tag appeared. Do not invent a tag.
If you maintain a pure-Python package, the risk is PEP 686 and lazy import interacting with your __init__.py side effects. Import the package under 3.15 with warnings turned into errors. If a locale encoding assumption pops, that is the Windows user you would have learned about in December.
If something explodes, file it against 3.15.0 with a repro, not against “lazy imports” as a scapegoat. rc3 existed because the core team already treated lazy-import bugs as release blockers. Your bug deserves the same specificity.
The calendar moved two weeks. The ABI did not. Build the wheel. Then go back to the security releases on the interpreter you actually run. If python -V still prints rc3 after lunch, you did not build the wheel. You built a story about the wheel.
Discussion
Leave a comment
No comments yet
Be the first to start the conversation.