Q&A library Python

How do I write my own context manager in Python?

Python Advanced contextlibcontext-managerwith

Short answer

Write a generator decorated with contextlib.contextmanager, putting setup before yield and cleanup after it. The cleanup runs even if the block raises, so it is the reliable place for teardown.

The short version

The easiest way to write a context manager is a generator decorated with @contextlib.contextmanager. Put the setup code before a single yield, and the cleanup code after it. Whatever you yield becomes the value bound by as. The cleanup after the yield runs even if the with block raises, which is exactly what makes it reliable for teardown.

A worked example

from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f"<{name}>")      # setup
    yield                    # the block runs here
    print(f"</{name}>")     # cleanup

with tag("section"):
    print("content")
<section>
content
</section>

The code before yield runs on entry, the block runs where yield sits, and the code after yield runs on exit.

Yielding a value

Whatever you yield is what as binds, so a context manager can hand a resource to the block.

@contextmanager
def opened(path):
    f = open(path)
    try:
        yield f            # the block gets the file
    finally:
        f.close()          # always closes, even on error

with opened("notes.txt") as f:
    data = f.read()

Why the try/finally matters

Wrap the yield in try/finally when the cleanup must always happen. If the block raises, the exception propagates through the yield, and only a finally guarantees the cleanup still runs. Without it, an error in the block would skip the teardown and leak the resource.

What problem this solves

Context managers capture the "always do this afterwards" pattern: open then close, acquire then release, start then stop. Putting the teardown in a context manager means callers cannot forget it, because the with block runs it automatically. This is why files, locks and connections are used with with.

The class-based alternative

You can also write a context manager as a class with __enter__ and __exit__ methods. The generator form is usually shorter and clearer for simple cases, while the class form gives more control, for example to inspect or suppress an exception in __exit__. Reach for the class form when the cleanup logic needs to react to whether an error occurred.

Temporary state changes

A common real use is temporarily changing something and restoring it afterwards, such as switching a setting for the duration of a block.

@contextmanager
def temporary(obj, attr, value):
    old = getattr(obj, attr)
    setattr(obj, attr, value)
    try:
        yield
    finally:
        setattr(obj, attr, old)   # restore no matter what

This guarantees the original value is restored even if the block raises, which is far safer than setting it back by hand after the block.

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project