Skip to main content
🧷
PEP 343 · Speedrun · Python 2.5 (2005)

PEP 343 in 90 seconds — with statement and the death of try/finally

Resource lifecycles, finally automatic.

Pre-3.0, releasing a file / lock / DB connection meant `try/finally` boilerplate. One forgotten `finally` and your handle leaked until the GC got bored: ```python f = open("data.csv") try: process(f) finally: f.close() # easy to forget on the 100th refactor ``` PEP 343 (Python 2.5, 2005 — backported even further than you'd think) added the `with` statement and the **context manager protocol** (`__enter__` / `__exit__`). The interpreter guarantees `__exit__` runs even if your code raises.

Any class with `__enter__` + `__exit__` is a context manager. `contextlib.contextmanager` lets you write them as generators: ```python from contextlib import contextmanager @contextmanager def timer(): import time t0 = time.perf_counter() yield # control returns here while body runs print(f"{time.perf_counter() - t0:.3f}s") ``` Every library that handles a resource — files, locks, transactions, HTTP sessions, GPU contexts — leans on this. It's so foundational that 3.10 added `async with` for the asyncio side.

Before — try/finally boilerplate, one mistake away from a leak
# Pre-PEP-343 — manual lifecycle
import threading

lock = threading.Lock()

lock.acquire()
try:
    do_critical_work()
finally:
    lock.release()  # forget this and your thread holds it forever

# And the same shape for files, DB connections, sockets, GPU contexts.
After — with statement, exit guaranteed
import threading

lock = threading.Lock()

# Context manager handles acquire + release automatically.
with lock:
    do_critical_work()
# Released here, even if do_critical_work raised.

# Same shape, every resource type:
with open("data.csv") as f:
    process(f)

# Or build your own:
class TimerCtx:
    def __enter__(self):
        self.events = []
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.events.append("exit")
        return False  # don't suppress exceptions

🎯 Predict the output

What does this print? `__exit__` runs BOTH when the body returns normally AND when it raises — guaranteed.

class TimerCtx:
    def __enter__(self):
        self.events = []
        self.events.append("enter")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.events.append("exit")
        return False

with TimerCtx() as t:
    t.events.append("inside")
    print(t.events)
print(t.events)
Context managers & resources → Foundations 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