Skip to content

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 exit instead of exit() 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() (use locale.setlocale(locale.LC_ALL, ""))

Deprecated in 3.13

  • pathlib.PurePath.is_relative_to() argument passing changed behavior
  • asyncio high-level API functions with loop parameter