What Are Context Managers?
A context manager handles setup and teardown of resources around a block of code. The with statement ensures cleanup happens even if an exception is raised — no try/finally boilerplate required.
# Without context manager
f = open("data.txt")
try:
content = f.read()
finally:
f.close()
# With context manager
with open("data.txt") as f:
content = f.read()
# File is automatically closed here, even on exception
Writing Custom Context Managers
class Timer:
def __enter__(self):
import time
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.perf_counter() - self.start
print(f"Elapsed: {self.elapsed:.3f}s")
return False # don't suppress exceptions
with Timer() as t:
do_expensive_work()
print(f"Done in {t.elapsed:.3f}s")
contextlib.contextmanager
The @contextmanager decorator lets you write context managers as generators — much less boilerplate than the class approach:
from contextlib import contextmanager
import tempfile, os
@contextmanager
def temp_directory():
tmpdir = tempfile.mkdtemp()
try:
yield tmpdir # execution enters the with block here
finally:
shutil.rmtree(tmpdir) # always cleaned up
with temp_directory() as tmpdir:
# work with tmpdir
Path(tmpdir, "output.txt").write_text("results")
Async Context Managers
from contextlib import asynccontextmanager
@asynccontextmanager
async def db_transaction(pool):
async with pool.acquire() as conn:
async with conn.transaction():
yield conn
async def create_user(pool, name):
async with db_transaction(pool) as conn:
await conn.execute("INSERT INTO users(name) VALUES($1)", name)
Frequently Asked Questions
Can I use multiple context managers in one with statement?
Yes. with open("in.txt") as fin, open("out.txt", "w") as fout: opens two files in a single statement. Both are closed on exit in reverse order.
What does returning True from __exit__ do?
It suppresses any exception that occurred in the with block. Return False (or None) to let exceptions propagate normally.