Python Context Managers Guide — with Statement and Custom Managers

Learn Python context managers: the with statement, contextlib, writing custom __enter__/__exit__, and async context managers for resource management.

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.