Context Managers & with
TypeScript has no with — Python does
Section titled “TypeScript has no with — Python does”TypeScript has no with keyword. When you need to acquire a resource and guarantee it is released — a file handle, a database connection, a lock — you write try/finally. You set up the resource before the try, and you clean it up in the finally block. This works, but it requires you to remember the pattern and write it correctly every time.
Python’s with statement encapsulates the setup and teardown inside the object itself. The object defines what happens on entry and what happens on exit, and Python guarantees the exit runs — even if an exception is raised inside the block. The cleanup logic lives in the resource, not scattered across every call site.
Class-based context managers
Section titled “Class-based context managers”A class becomes a context manager by implementing two dunder methods: __enter__ and __exit__. Python calls __enter__ when execution enters the with block and __exit__ when it leaves — for any reason.
// TypeScript: try/finally for guaranteed cleanupclass ManagedResource { constructor(private name: string) {}
acquire(): this { console.log(`Acquiring ${this.name}`); return this; }
release(): void { console.log(`Releasing ${this.name}`); }
use(): void { console.log(`Using ${this.name}`); }}
const resource = new ManagedResource("database connection");resource.acquire();try { resource.use();} finally { resource.release(); // must remember to call this}# Python: with statement handles acquire and release automaticallyclass ManagedResource: def __init__(self, name): self.name = name
def __enter__(self): print(f"Acquiring {self.name}") return self # bound to the 'as' variable
def __exit__(self, exc_type, exc_val, exc_tb): print(f"Releasing {self.name}") return False # do not suppress exceptions
with ManagedResource("database connection") as resource: print(f"Using {resource.name}")# release is guaranteed even if an exception occursThe value returned by __enter__ is bound to the variable after as. The as clause is optional — you can write with ManagedResource("x"): if you do not need a reference to the resource inside the block.
contextlib: generators as context managers
Section titled “contextlib: generators as context managers”Writing a full class for a simple context manager is often overkill. The contextlib module provides a @contextmanager decorator that lets you write a context manager as a generator function. Everything before yield is the setup; everything after yield is the teardown.
// TypeScript: a utility wrapper function with try/finallyfunction withTimer<T>(label: string, fn: () => T): T { console.log(`[${label}] starting`); try { return fn(); } finally { console.log(`[${label}] done`); }}
const result = withTimer("my block", () => { let total = 0; for (let i = 0; i < 1000; i++) total += i; console.log(`sum = ${total}`); return total;});from contextlib import contextmanager
@contextmanagerdef timer(label): print(f"[{label}] starting") yield # execution of the with block happens here print(f"[{label}] done")
with timer("my block"): total = sum(range(1000)) print(f"sum = ${total}")Try it
Section titled “Try it”from contextlib import contextmanager
class ManagedResource: def __init__(self, name): self.name = name
def __enter__(self): print(f"Acquiring {self.name}") return self
def __exit__(self, exc_type, exc_val, exc_tb): print(f"Releasing {self.name}") return False
with ManagedResource("database connection") as resource: print(f"Using {resource.name}")
@contextmanagerdef timer(label): print(f"[{label}] starting") yield print(f"[{label}] done")
with timer("my block"): total = sum(range(1000)) print(f"sum = {total}")Loading Python runtime (first run only)…