Skip to content

Context Managers & with

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.

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
// TypeScript: try/finally for guaranteed cleanup
class 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
# Python: with statement handles acquire and release automatically
class 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 occurs

The 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
// TypeScript: a utility wrapper function with try/finally
function 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;
});
Python
from contextlib import contextmanager
@contextmanager
def 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}")
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}")
@contextmanager
def timer(label):
print(f"[{label}] starting")
yield
print(f"[{label}] done")
with timer("my block"):
total = sum(range(1000))
print(f"sum = {total}")
What two dunder methods must a class implement to work as a context manager?
What value is bound to the variable after "as" in a with statement?
In a @contextmanager function, where does the body of the with block execute?
How does __exit__ suppress an exception that occurred inside the with block?