Developer ToolsProgramming LanguagesPython

Python 3.15 RC2 Ships: What Free-Threaded Python Means for Your Code

Python 3.15 free-threading: python3.15t interpreter with GIL unlock and parallel thread visualization
Python 3.15 ships October 1 with stable ABI for free-threaded builds

Python 3.15.0 ships October 1. RC2 landed September 1, and the headline traveling fastest through developer circles — “the GIL is finally gone” — is wrong in exactly the way that will catch you off guard. Your standard python3.15 interpreter still has the GIL. The free-threaded build is python3.15t, a separate binary, and it stays opt-in. What 3.15 actually ships is the infrastructure that makes dropping the GIL viable at scale: a Stable ABI for free-threaded C extensions, UTF-8 as the universal encoding default, and lazy imports. Quieter than “GIL removed.” More likely to break your production code.

Your python3.15 Still Has the GIL

Let’s be precise about the timeline. Free-threading in CPython moved from experimental (3.13) to officially supported in 3.14 via PEP 779. Python 3.15 does not change that status — it refines it. The default interpreter has the GIL. The python3.15t build does not. You choose.

The performance story improved. Python 3.13t carried roughly a 40% overhead compared to regular CPython because the specializing adaptive interpreter had been disabled for safety. Python 3.14t re-enabled it and brought that penalty down to 5–10% on most workloads (closer to 1% on Apple Silicon). Python 3.15t continues on that trajectory. You’re no longer paying a brutal tax just to run without the GIL.

PEP 803: The Real Unlock for the Ecosystem

The bigger story in 3.15 is PEP 803, which introduces abi3t — a Stable ABI variant for free-threaded builds. Before this, every package that ships C extensions had to build and publish separate cp315t wheels for the free-threaded interpreter. That’s a maintenance burden most maintainers couldn’t justify, which is why the python3.15t ecosystem was sparse.

With abi3t, an extension author can compile once and produce a wheel that works on both GIL and no-GIL builds. PyO3, CFFI, and Cython support it. The catch: it’s not automatic. Extension authors need to switch to PEP 697-style APIs and replace PyInit_ entry points with the new PyModExport_* hook. The Python docs include a migration guide for abi3t.

The ecosystem won’t flip overnight. But abi3t removes the structural blocker. Expect PyPI wheel coverage to grow through 2026 and into 2027.

UTF-8 Default Encoding: The Change Most Likely to Bite You

This one has nothing to do with free-threading, which is exactly why it catches people.

Python 3.15 finalizes PEP 686: UTF-8 is now the default encoding for open() on all platforms, regardless of system locale. Every open(file, "r") without an explicit encoding= argument now reads as UTF-8. So does subprocess.run(text=True) without an encoding.

On macOS and Linux, this changes almost nothing. On Windows — where cp1252, cp932, and regional code pages are still common defaults — this is a silent data corruption risk for any code reading locale-encoded files without specifying the encoding. Find the exposure in your codebase:

# Detect missing encoding arguments
grep -rn "open(" src/ --include="*.py" | grep -v "encoding="

# Run with EncodingWarning as an error
python -W error::EncodingWarning your_app.py

PYTHONUTF8=0 or -X utf8=0 restores locale-based encoding temporarily. But the long-term fix is explicit encoding= arguments throughout your file I/O.

Lazy Imports, frozendict, and Tachyon

Python 3.15 brings a few quality-of-life additions worth knowing:

  • Lazy imports — the new lazy soft keyword defers an import until the name is first accessed. For CLI tools that pull in heavy dependencies but only use a fraction of them per invocation, startup time drops measurably.
  • frozendict — an immutable, hashable built-in dict. Cleaner than types.MappingProxyType for read-only config objects and thread-safe data passing.
  • Tachyon profiler — a statistical sampling profiler added as profiling.sampling. Works on live processes without restart. More useful than cProfile for production diagnostics.

One removal to catch before October 1: datetime.utcnow() is gone. It was deprecated in 3.12. Replace it with datetime.now(timezone.utc) now, or the runtime will find it for you.

What to Do Before October 1

RC2 is the last planned release candidate. Here’s the pre-release checklist:

1. Run your full test suite against RC2. Bugs reported now get fixed before October 1. Use pyenv or the official RC2 installer.

2. Test your C extensions under python3.15t.

# Verify C extension under free-threaded build
python3.15t -c "import your_extension; print(apos;abi3t compatible apos;)"

# Run tests under free-threaded build
python3.15t -m pytest tests/ -x --tb=short

3. Enable EncodingWarning and fix file I/O. Search for open( calls missing encoding= and address them before the UTF-8 default silently changes behavior in production.

The right posture for October 1: test RC2 in staging, evaluate the free-threaded build, but don’t rush python3.15t into production. The ecosystem is catching up — the free-threading compatibility tracker shows which packages are already abi3t-ready. Python 3.16 is when coverage will be broad enough for most teams to move without friction.

For where free-threading stood in 3.14, see our earlier coverage on Python 3.14 free-threading on RHEL. And check the official what’s new in Python 3.15 for the full changelog before you upgrade.

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 *