NewsProgramming LanguagesPython

Python 3.15: Lazy Imports, frozendict, and a Working JIT

Python 3.15 release featuring lazy imports, frozendict built-in type, and JIT compiler improvements

Python 3.15 went final on October 9, built from 5,643 commits across 1,012 contributors. Three features stand out: lazy imports are now a language keyword, frozendict and sentinel are built-ins, and the JIT compiler finally posts numbers worth discussing. One thing will break your code if you are not paying attention: the default encoding is now UTF-8, everywhere, on every platform.

UTF-8 Is Now the Default — Check Your Code Before You Upgrade

This is the one to act on immediately. Any call to open(), read_text(), or write_text() without an explicit encoding= argument now uses UTF-8 regardless of system locale. On macOS and Linux this usually changes nothing. On Windows — particularly on machines that defaulted to cp1252 or a regional encoding — this is a silent behavior change that will surface as garbled text or decode errors in production.

Run the audit before you upgrade:

python -X warn_default_encoding script.py

That flag surfaces every call site that relies on implicit encoding. Fix them with explicit encoding="utf-8" arguments. If you need to hold off, PYTHONUTF8=0 restores the old behavior temporarily. This is PEP 686 — long overdue, but it will catch teams that assumed their locale was already UTF-8 and never verified.

Lazy Imports: Real Startup Gains With One Gotcha

Python 3.15 adds a lazy soft keyword via PEP 810. The syntax is straightforward:

lazy import json
lazy from pathlib import Path

print("App starting...")  # json and Path not loaded yet
data = json.loads('{"key": "value"}')  # json loads here

The module name gets bound immediately, but loading waits until first use. For CLI tools and serverless functions with large dependency trees, this meaningfully reduces cold start times. Enable globally with -X lazy_imports=all or the PYTHON_LAZY_IMPORTS environment variable.

The gotcha: any import that has side effects — logging configuration, plugin registration, monkeypatching — now runs on first use rather than at import time. Framework and library authors need to audit their import chains before tagging releases for 3.15. Application developers using those libraries need to test carefully.

frozendict and sentinel Are Now Built-Ins

Two features that developers have requested for years arrived in the same release. frozendict is an immutable, hashable dictionary — not a dict subclass, but it supports all read operations and can be used as a dictionary key or set member:

config = frozendict(host="localhost", port=5432)
hash(config)  # works
settings = {config: "production"}  # also works

config["port"] = 3306  # TypeError

Migration note: isinstance(x, dict) returns False for frozendict. Switch to isinstance(x, (dict, frozendict)) or check against collections.abc.Mapping. The standard library — json, pickle, pprint — already accepts frozendict natively.

sentinel replaces the _MISSING = object() pattern with something that has a readable repr, survives pickling, and cooperates with type checkers:

NOT_FOUND = sentinel("NOT_FOUND")
result = cache.get(key, NOT_FOUND)
if result is NOT_FOUND:
    result = compute(key)

The JIT: Useful Numbers, Still Experimental

Python's experimental JIT compiler delivers 7–8% geometric mean improvement on x86-64 Linux and 11–12% on AArch64 macOS. Windows 64-bit now uses the tail-calling interpreter by default, adding another 15–20% on CPU-bound workloads. These are real numbers — but they come from CPU-bound benchmarks. Web applications and anything I/O-bound will see far less. The JIT is still opt-in, still requires LLVM 21, and is now debuggable for the first time.

Worth enabling for data processing pipelines and computation-heavy code. Not a reason to rewrite anything.

Quick Migration Checklist

  • Run python -X warn_default_encoding and add explicit encoding= to every flagged call
  • Audit imports with side effects before enabling lazy imports globally
  • Update isinstance(x, dict) checks if adopting frozendict
  • Check the full changelog for removed APIs — sre_* modules, glob.glob0/1, pathlib.PurePath.is_reserved() are gone
  • Start planning for asyncio event loop policy removal in 3.16 if you use Django, aiohttp, or Twisted

Python 3.15 is the most practically useful Python release in several years. The new built-ins solve real problems, lazy imports change how you structure large applications, and the JIT is starting to matter. Upgrade — but run that encoding audit first.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News