What's New in Python 3.13¶
3.13
Python 3.13 shipped in October 2024 with two experimental-but-landmark features: a free-threaded CPython build (PEP 703 — the GIL is finally optional) and an experimental JIT compiler. Plus a significantly upgraded interactive REPL.
Summary¶
| Category | Change |
|---|---|
| CPython | Free-threaded build (GIL-disabled) — PEP 703 |
| CPython | Experimental JIT compiler |
| REPL | Colorized output, multi-line editing, help() rewrite |
| Standard Library | copy.replace() for copying with modifications |
| Typing | typing.TypeIs, typing.ReadOnly |
| Deprecations | Py_BEGIN_ALLOW_THREADS legacy macros deprecated |
PEP 703 — Making the GIL Optional¶
This is possibly the biggest change to CPython's architecture since Python 2→3. The Global Interpreter Lock (GIL) prevents multiple Python threads from executing Python bytecode simultaneously. For I/O-bound work this doesn't matter, but for CPU-bound multi-threaded code, the GIL has been a performance ceiling for 30 years.
Python 3.13 ships a separate GIL-disabled build. You opt in at build time (not at runtime in the stable build):
# Install GIL-disabled Python (CPython build flag)
PYTHON_GIL=0 python3.13 yourscript.py # if your distro supports it
# Check at runtime
import sys
print(sys._is_gil_enabled()) # True (normal), False (free-threaded build)
Experimental
The free-threaded build is functional but not stable for production. Many C extensions aren't thread-safe yet. If you install Python from python.org, there's a separate installer for the "free-threaded" variant.
New and Improved REPL¶
The interactive interpreter got a real upgrade:
- Colorized output — syntax highlighting in the REPL prompt
- Multi-line editing — press
[Enter]on an incomplete expression to continue; the REPL knows you're not done - Exit shorthand — type
exitinstead ofexit()to actually exit help()rewrite — cleaner formatting, works better in narrow terminals
# In 3.13 REPL, this actually exits:
>>> exit
# (previously it would print "Use exit() or Ctrl-D to exit")
copy.replace() — PEP 698¶
Makes a copy of an object with specified fields replaced. Works for anything that implements __replace__() — including dataclasses and namedtuples out of the box.
from dataclasses import dataclass
import copy
@dataclass
class Point:
x: float
y: float
p1 = Point(1.0, 2.0)
p2 = copy.replace(p1, y=5.0)
print(p2) # Point(x=1.0, y=5.0)
Typing Improvements¶
typing.TypeIs¶
A narrower alternative to TypeGuard. The difference: TypeIs[X] guarantees the return False branch narrows the type too.
from typing import TypeIs
def is_str(val: object) -> TypeIs[str]:
return isinstance(val, str)
val: str | int = get_value()
if is_str(val):
print(val.upper()) # val is str here
else:
print(val + 1) # val is int here
typing.ReadOnly¶
For TypedDict fields that shouldn't be written to:
from typing import TypedDict, ReadOnly
class Config(TypedDict):
host: ReadOnly[str] # Can't be changed after creation
port: int
Deprecations and Removals¶
Removed in 3.13¶
locale.resetlocale()(uselocale.setlocale(locale.LC_ALL, ""))
Deprecated in 3.13¶
pathlib.PurePath.is_relative_to()argument passing changed behaviorasynciohigh-level API functions withloopparameter