Script Valley
Python: Complete Language Course
Advanced Python PatternsLesson 6.3

Python context managers — writing your own with statement

__enter__, __exit__, contextlib.contextmanager, exception suppression in __exit__, resource management pattern, practical examples

Context Managers

A context manager implements __enter__ and __exit__. with obj as x calls __enter__, runs the block, then calls __exit__ regardless of exceptions.

class Timer:
    import time

    def __enter__(self):
        self.start = __import__("time").perf_counter()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.elapsed = __import__("time").perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.4f}s")
        return False  # do not suppress exceptions

with Timer() as t:
    total = sum(range(1_000_000))
# Elapsed: 0.0XXXs

Using contextlib

from contextlib import contextmanager

@contextmanager
def managed_file(path, mode):
    f = open(path, mode)
    try:
        yield f
    finally:
        f.close()

with managed_file("out.txt", "w") as f:
    f.write("hello")

Return True from __exit__ to suppress an exception; False (or None) lets it propagate. The @contextmanager decorator lets you write a context manager as a generator — put setup before yield and teardown after.

The context manager pattern solves the setup/teardown problem universally. Database connections, locks, network sockets, temporary directories, and mock patches all use it. The contextlib module provides many ready-made context managers: suppress(ExceptionType) to silently ignore specific exceptions, redirect_stdout to capture output in tests, and ExitStack to manage a variable number of context managers dynamically. Writing your own is straightforward and makes resource-management code clean and reusable across your entire codebase.

The @contextmanager decorator is the quickest way to create a context manager without a full class. Put setup code before yield and teardown in a finally block after it — the finally guarantees teardown even when an exception propagates through the with block. Context managers are also composable: with open(a) as f1, open(b) as f2: manages both files in one statement, equivalent to nesting two with blocks.

Up next

Python type hints and how to use them

Sign in to track progress