What is a context manager in Python?

A context manager (used with the `with` statement) ensures setup and cleanup code always run, even if an exception occurs - most commonly used for files, locks, and connections.

Python

Overview

The `with` statement guarantees that resources are properly acquired and released. When the block exits - normally or due to an exception - the context manager's cleanup code runs automatically. This eliminates the common bug of forgetting to close a file or release a lock in a `finally` block.

Key Points

  • •`with open("file") as f:` automatically calls `f.close()` when the block exits.
  • •Context managers implement `__enter__` (setup) and `__exit__` (cleanup) methods.
  • •The `as` clause captures the return value of `__enter__`.
  • •`contextlib.contextmanager` lets you create one with a generator instead of a class.
  • •Multiple managers in one `with`: `with open(a) as f, open(b) as g:`.

Examples

File handling (the classic example)

python
# Without context manager - file may not be closed if an error occurs
f = open('data.txt')
try:
    data = f.read()
finally:
    f.close()

# With context manager - clean and safe
with open('data.txt') as f:
    data = f.read()
# f.close() is called automatically, even if f.read() raises

Database connection pattern

python
import sqlite3

with sqlite3.connect('app.db') as conn:
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users")
    rows = cursor.fetchall()
# connection closed automatically; transaction committed or rolled back

Build a custom context manager with @contextmanager

python
from contextlib import contextmanager
import time

@contextmanager
def timer(label):
    start = time.perf_counter()
    try:
        yield          # code inside the 'with' block runs here
    finally:
        elapsed = time.perf_counter() - start
        print(f"{label}: {elapsed:.4f}s")

with timer("database query"):
    results = db.execute("SELECT ...")
# database query: 0.0123s

Suppress specific exceptions

python
from contextlib import suppress

# Instead of try/except just to ignore FileNotFoundError:
with suppress(FileNotFoundError):
    os.remove('temp.txt')   # no error if file doesn't exist

Pro Tip

`with` is not just for files. Use it for threading locks (`with lock:`), temporary directories (`with tempfile.TemporaryDirectory() as tmp:`), database transactions, mocking in tests (`with mock.patch(...)`), and any resource that needs guaranteed cleanup.