GGistDev

Context Managers in Python

Context managers manage resources by defining enter/exit semantics used by the with statement.

Using with files and locks

with ensures cleanup even if exceptions occur.

from threading import Lock
lock = Lock()

with open("data.txt", encoding="utf-8") as f:
    data = f.read()

with lock:
    critical_section()

Custom context managers (class)

Implement __enter__ and __exit__.

class Timer:
    def __enter__(self):
        import time
        self.start = time.perf_counter()
        return self
    def __exit__(self, exc_type, exc, tb):
        import time
        self.elapsed = time.perf_counter() - self.start
        # return True to suppress exceptions (rare)

with Timer() as t:
    work()
print(t.elapsed)

contextlib utilities

Use contextlib.contextmanager for simple managers made from generators.

from contextlib import contextmanager

@contextmanager
def opened(path, mode="r", **k):
    f = open(path, mode, **k)
    try:
        yield f
    finally:
        f.close()

Stack managers cleanly with ExitStack.

from contextlib import ExitStack
with ExitStack() as stack:
    f1 = stack.enter_context(open("a"))
    f2 = stack.enter_context(open("b"))

Async context managers

async with works with objects defining __aenter__/__aexit__.

Best practices

  • Keep critical sections small; avoid long‑running work inside with
  • Prefer contextlib helpers for concise, correct managers
  • Avoid suppressing exceptions unless you re‑raise/log appropriately

Summary

  • with guarantees cleanup; write class‑ or generator‑based managers
  • Use ExitStack for dynamic sets; async with for async resources