Python Context Managers

Guarantee setup and teardown with the with statement and contextlib

Overview

Context managers define what happens when entering and leaving a with block—typically acquiring and releasing resources. Files, locks, and database connections commonly use this pattern to prevent leaks even when errors occur.

Syntax / Usage

# Built-in: files
with open("data.txt") as f:
    data = f.read()
# file automatically closed

# Multiple managers
with open("in.txt") as src, open("out.txt", "w") as dst:
    dst.write(src.read())

# Custom context manager (class)
class Timer:
    def __enter__(self):
        import time
        self.start = time.perf_counter()
        return self

    def __exit__(self, exc_type, exc, tb):
        import time
        print(f"Elapsed: {time.perf_counter() - self.start:.4f}s")
        return False  # do not suppress exceptions

with Timer():
    sum(range(1_000_000))

# contextlib decorator
from contextlib import contextmanager

@contextmanager
def temporary_env(key, value):
    import os
    old = os.environ.get(key)
    os.environ[key] = value
    try:
        yield
    finally:
        if old is None:
            del os.environ[key]
        else:
            os.environ[key] = old

Examples

Database transaction pattern (conceptual):

@contextmanager
def transaction(conn):
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise

Suppress errors for optional cleanup:

from contextlib import suppress

with suppress(FileNotFoundError):
    Path("temp.cache").unlink()

Common Mistakes

  • Returning True from __exit__ and silently swallowing exceptions
  • Not using context managers for locks, sockets, or DB connections
  • Implementing __enter__ without matching cleanup in __exit__

See Also

python-exceptions python-file-io python-decorators