Skip to main content
🔓
PEP 703 · Speedrun · Python 3.13 (2024, experimental)

PEP 703 in 90 seconds — Python without the GIL is real (and optional)

32 years of GIL — and now an exit door.

The GIL (Global Interpreter Lock) is why pure-Python multi-threading doesn't speed up CPU-bound work: only one thread can execute Python bytecode at a time. For 32 years this was a hard ceiling. Workarounds (multiprocessing, releasing the GIL in C extensions, asyncio for IO-bound) exist; the actual primitive most other languages have (threads that scale CPU-bound) did not.

PEP 703 (Python 3.13, 2024) ships an OPTIONAL no-GIL interpreter build (`python3.13t`, the 't' for 'threaded'). Pure-Python threads now scale linearly on CPU work — at the cost of about 10% single-thread slowdown and a wave of subtle race-condition bugs in code that quietly relied on GIL atomicity. It's officially experimental through 3.13–3.15. By 3.16 (~2027) the default may shift, depending on how the ecosystem holds up. Senior devs need to know it's coming.

Before — GIL-protected pure-Python increment is atomic-enough
# Python ≤ 3.12 (or 3.13 default build)
import threading

counter = 0
def bump():
    global counter
    for _ in range(100_000):
        counter += 1

threads = [threading.Thread(target=bump) for _ in range(8)]
for t in threads: t.start()
for t in threads: t.join()

print(counter)
# Output: 800000  (GIL serialises increments → no race)
After — no-GIL build, same code is now a race
# Python 3.13t (no-GIL) — same code, same threads:
#   counter += 1  is NOT atomic without the GIL.
#   Threads interleave: read counter → add 1 → write back.
#   Lost updates are now real.

print(counter)
# Output: 487213  (or any number under 800000, varies per run)

# Fix: use threading.Lock() or atomic counter from C extension.

🎯 Predict the output

On the no-GIL build, what's the maximum SAFE value `counter` could end at across 8 threads × 100,000 increments? (Hint: races lose updates, never gain them.)

# 8 threads × 100,000 iterations each.
# Each iteration: counter += 1 (read → add → write, non-atomic).
# What's the MAX value of counter after all threads finish?
Concurrency & async internals → Senior track

Or speedrun another PEP

PEP 572Assignment as an expression — 3 lines become 1.
PEP 572 in 90 seconds — the walrus that ate your for-loop
PEP 484Python stays dynamic — but you can opt into typing.
PEP 484 in 90 seconds — type hints, without TypeScript-envy
PEP 622Switch statements are a 5% feature. Pattern matching is the 95%.
PEP 622 in 90 seconds — match / case isn't just a switch