PythonMastery

Create a context manager in Python

Write a class with __enter__ and __exit__ so it works with the with statement. Example: a timer. Also the shorter way with contextlib.

A context manager is an object that works with the with statement. Python calls its __enter__ method at the start of the block and its __exit__ method at the end, even when the block raises an error. open() is the best-known example: its __exit__ closes the file.

The program

python
import time


class Timer:
    def __init__(self, label):
        self.label = label

    def __enter__(self):
        self.start = time.perf_counter()
        return self  # this is the value after "as"

    def __exit__(self, exc_type, exc_value, traceback):
        self.elapsed = time.perf_counter() - self.start
        print(f"{self.label}: {self.elapsed:.1f} s")
        return False  # do not hide errors


with Timer("Short pause") as t:
    time.sleep(0.1)

print("Stored on the object:", round(t.elapsed, 1))
example output · yours will differ
Short pause: 0.1 s
Stored on the object: 0.1

How it works

The shorter way: contextlib

For simple cases, @contextmanager turns a generator function into a context manager. The code before yield is the "enter" part, and the code in finally is the "exit" part:

python
from contextlib import contextmanager


@contextmanager
def section(title):
    print(f"--- {title} ---")
    try:
        yield
    finally:
        print(f"--- end of {title} ---")


with section("Report"):
    print("Sales were up.")

try:
    with section("Broken"):
        raise ValueError("bad data")
except ValueError as e:
    print("Caught:", e)
output
--- Report ---
Sales were up.
--- end of Report ---
--- Broken ---
--- end of Broken ---
Caught: bad data

The second block raised an error, but the closing line was still printed. The finally makes sure of that, in the same way that __exit__ always runs.

Learn the idea: Context Managers: Beyond with-open, Object-Oriented Programming