Python 3.15 RC3 locks in lazy imports and default UTF-8: what to review before migrating legacy systems
The third and final release candidate of Python 3.15, announced in recent days by the Python Software Foundation, delayed the final release to October 9 due to last-minute bugs in lazy imports. For those who maintain production code, the list of changes worth testing now is longer than it seems.
The third and final release candidate of Python 3.15, announced in recent days by the Python Software Foundation, delayed the final release to October 9 due to last-minute bugs in lazy imports. For those who maintain production code, the list of changes worth testing now is longer than it seems.
Python 3.15 entered its final stretch. The release team published on the official Python Insider blog the third release candidate of the series, named 3.15.0rc3, announcing that the final release, previously expected this week, was postponed to October 9, 2026. The reason: last-minute bugs ("last-minute lazy-import release blockers") considered serious enough to warrant another round of testing before becoming a stable version.
The rc3 brings together 156 fixes, build improvements, and documentation adjustments made by 82 contributors since rc2. From here on, the project's policy is strict: only reviewed bug fixes get in. No further ABI changes are planned for the rest of the 3.15 series. In other words, what's in rc3 is, for practical purposes, the behavior that goes to production.
Why the delay matters more than the delay itself
The detail that matters to whoever maintains legacy systems isn't the one-week postponement, it's the reason behind it. The bugs that held back the final release are related to PEP 810, the explicit lazy imports feature that Python 3.15 introduces to reduce startup time.

Explicit lazy imports allow you to declare that a module should only actually be loaded when one of its names is used for the first time, instead of at the moment of import. This tackles a real problem in large applications: CLI scripts, Lambdas, and short-lived processes that pay the cost of importing dozens of dependencies even when they only use a fraction of them.
The point of attention is exactly that: it's a change in the order of code execution, not just a silent optimization. Projects that depend on side effects at import time (plugin registration, database driver initialization, logging configuration) may behave differently if they adopt lazy imports without reviewing these implicit dependencies. The fact that the team itself found "last-minute" bugs in this area reinforces that this isn't a feature to enable by default in production code without dedicated testing.
UTF-8 as default: the breaking change that goes unnoticed
Another change in the series that weighs more for those maintaining legacy systems than for those starting a new project is PEP 686, which makes UTF-8 the default encoding in Python, replacing the old dependency on the operating system's locale.
In legacy Brazilian environments, this carries concrete weight:
- ETL scripts and file processing that historically ran on servers with
pt_BR.ISO-8859-1orpt_BR.CP1252locale may have decoded text incorrectly without any error at all until now, because Python followed the system locale. - Code that opens files with
open()without explicitly specifyingencoding=is the main candidate for a change in behavior. - Tests that pass in the development environment (usually already in UTF-8) and fail in production (server with legacy locale) tend to show up only after deployment.
In short: if the system has open() without explicit encoding scattered throughout the code, this is the Python 3.15 change that deserves a grep before anything else.
What else is on the series' change list
Besides lazy imports and default encoding, the Python Insider release notes list a set of new features that make up the full picture of 3.15 versus 3.14:
| Change | What it is | Production impact |
|---|---|---|
| PEP 810 (lazy imports) | Import deferred until first use of the name | May change the order of import side effects |
| PEP 686 (default UTF-8) | Default encoding no longer follows the OS locale | Risk of different decoding in legacy files |
PEP 814 (frozendict) | Immutable built-in dict type | Replaces homemade "frozen" dict patterns |
PEP 661 (sentinel) | Built-in type for sentinel values | Reduces the classic object() as sentinel hack |
| PEP 798 | Unpacking inside comprehensions | New syntax, no effect on existing code |
| Experimental JIT | +7-8% on x86-64 Linux, +11-12% on AArch64 macOS | Optional performance gain, still experimental |
The experimental JIT deserves a separate note: the gain of 7 to 8% geometric mean on Linux x86-64 and 11 to 12% on macOS AArch64, according to the announcement, is measured against the standard interpreter (and against the tail-calling interpreter, in the case of macOS). The compiler is still experimental, not enabled by default, and the recommendation remains to treat it as opt-in for those who want to test performance, not as a production baseline.
What to do with RC3 now
The release notes are direct about the role of RC3: it's a preview, not recommended for production, and the call to action is specific for those maintaining packages published on PyPI.
We strongly encourage maintainers of third-party Python projects to prepare their projects for 3.15 during this phase, and publish Python 3.15 wheels on PyPI to be ready for the final release of 3.15.0.
Python 3.15 release team (Hugo van Kemenade, Ned Deily, Steve Dower)
For those who maintain legacy systems (not published libraries), the equivalent roadmap is different: run the test suite against rc3 in an isolated environment, grep for open() without explicit encoding, map out import statements with side effects before considering lazy imports, and only then decide on migration timing. Since no further ABI changes are planned from here on, any wheel compiled against rc3 works with the final version, which reduces the risk for those already testing C extensions.
It's also worth noting a platform-specific warning: macOS 27.0 users may see IDLE and other tkinter applications freeze when opening menu dialogs, due to a change in operating system behavior that affects Tk across all current Python versions, not just 3.15. Those who depend on Tk-based tools on this system should follow issue #158053 on the CPython tracker before updating macOS.
With the final release set for October 9 and the ABI freeze already in effect, 3.15 is, in practice, defined. What remains is the work of those who maintain running code: separating what's a catalog novelty from what's a silent behavior change, and testing the latter before it shows up as a bug in production.
Translated from the Brazilian Portuguese original · Read the original
Node.js runs TypeScript without a build step: what native type-stripping unlocks and what still breaks in the backend
Since 2024, Node.js has been able to run .ts files by stripping types on the fly, without ts-node, tsx, or swc. But enums, namespaces with code, and decorators remain out of reach.