XT.PT Python → This story
XT.PT
⌕
News Python

Python 3.15.0 ships eight days late, and the lazy-import fixes are still coming

Why Python 3.15.0 slipped to October 9, which PEP 810 lazy-import bugs rc3 fixed, and what is still waiting for the first bugfix release.

code

Python 3.15.0 is out. The release page on python.org gives the date as "Oct. 9, 2026" and counts "5,643 commits from 1,012 contributors." That is eight days later than planned. When the last scheduled release candidate froze the ABI on September 1, the final was due on October 1.

The feature list did not change in those eight days. The schedule did, and the reason was the headline feature: explicit lazy imports, PEP 810.

A third release candidate nobody had scheduled

PEP 790, the 3.15 release schedule, lived in the python/peps repository with a single expected date for the final: "Thursday, 2026-10-01". The commit that changed it, titled "PEP 790: Add extra 3.15 rc3 and postpone 3.15.0 final", landed on October 2 at 21:28 UTC, a day after the original target had already passed. It added "3.15.0 candidate 3: Friday, 2026-10-02" and moved the final to "Friday, 2026-10-09".

The rc3 page opened "Following recent tradition, here's a surprise third release candidate" and gave the reason in one line:

We got some last-minute lazy-import release blockers, and it makes sense to include them in 3.15.0 final.

Python 3.15.0rc3 release page, python.org

The same page says rc3 contained "around 156 bugfixes, build improvements and documentation changes from 82 contributors since 3.15.0rc2," and repeats the ABI promise: "There will be no ABI changes from this point forward in the 3.15 series."

What the blockers were

The official changelog lists the lazy-import fixes under the rc3 heading. The two that matter most change what a program actually imports:

  • gh-157757: "Fix a lazy import a.b as c reading b off a instead of importing the module a.b. It now binds the submodule, and raises ModuleNotFoundError when no module backs the name."
  • gh-158140: "Resolve lazily imported sibling submodules independently, so accessing one does not import another merely because its declaration came last."

Two more tidy up bookkeeping and annotations. gh-155695 makes resolved names leave sys.lazy_modules "more consistently," and gh-156924 makes annotationlib.ForwardRef.evaluate() resolve lazy import proxies instead of handing them back.

The final itself added nothing. Its changelog section reads, in full: "There were no new changes in version 3.15.0."

The fixes did not stop at the final

The changelog's "Python next" section, the queue for the first bugfix release, already holds five more lazy-import entries:

  • gh-159288: a crash "when walking a very long chain of lazy import placeholders, as created by an aliased dotted import such as lazy import a.b.c as d." It now raises RecursionError instead.
  • gh-158824: module-level __getattr__() "being called before a module’s own lazy imports were resolved, which allowed it to shadow them."
  • gh-158820: a crash "when a lazy import is declared or resolved by a finalizer running during interpreter shutdown."
  • gh-158936: dataclass() failing on a string annotation that names an unresolvable lazy import.
  • gh-153888: unittest.mock.patch() with autospec, spec or spec_set on lazy-imported objects.

None of these is in 3.15.0. PEP 790 promises "Subsequent bugfix releases every two months" and, as of this writing, lists no date for 3.15.1.

A security fix in rc3 that can raise an error

One rc3 entry under Security changes behavior on purpose. ssl.SSLContext.wrap_bio() now validates its arguments the way wrap_socket() does, and the changelog spells out the old failure: "a context with check_hostname enabled and no server_hostname passed to wrap_bio() now raises ValueError instead of completing a handshake that verified the certificate chain without verifying the peer's identity, with no indication that the check had been skipped." Code that passed no hostname with check_hostname on was silently getting less verification than it asked for. On 3.15 it gets an exception.

What to do with it

If you adopt lazy import now, the aliased dotted form (lazy import a.b as c) is the one with the most history: one fix in rc3 and a crash fix still waiting for 3.15.1. Pin your test matrix to 3.15.0 and plan to retest on the first bugfix release. Extension wheels are not a reason to wait: the rc3 page says "Any binary wheels built against Python 3.15.0 release candidates will work with future versions of Python 3.15."

Primary sources: Python 3.15.0 release page, Python 3.15.0rc3 release page, Python 3.15 changelog, PEP 790, PEP 790 postponement commit, read 2026-10-16.

Corrections and source documents: contact the desk
Read next →
Read next
Supply chain · 4 min

npm trusted publishing configurations now expire after 48 hours, and issue_comment workflows can no longer publish

Go security · 5 min

Go 1.27.2 and 1.26.9 fix 15 vulnerabilities, seven of them in HTTP/2 and CONNECT handling