Warming up the neural circuits...
By the end of this chapter you will:
Resource leaks are silent until production pain begins: file handles stay open, locks are never released, temporary flags remain enabled, and partial failures leave dirty .
Context managers give you structured setup/teardown so cleanup happens even when exceptions are raised.
with manager as value: is conceptually:
manager = acquire_manager()
value = manager.__enter__()
try:
...
finally:
manager.__exit__(exc_type, exc, tb)This guarantee is why with is safer than manual setup/cleanup.
import time
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, exc_type, exc, tb):
elapsed
from contextlib import contextmanager
@contextmanager
def temporary_flag(state: dict, key: str, value: bool):
old = state.get(key)
state[key] = value
try
Useful helpers:
suppress(ValueError): ignore a specific expected exceptionredirect_stdout(io.StringIO()): capture outputclosing(obj): call close() automaticallynullcontext(): optional context These reduce custom code for common tasks.
from contextlib import ExitStack
paths = ["a.txt", "b.txt", "c.txt"]
with ExitStack() as stack:
files = [stack.enter_context(open(p, "w", encoding
Any that requires "remember to call close()" is a future bug. Prefer returning context managers so cleanup is enforced by structure.
| Need | Use |
|---|---|
| Complex stateful reusable manager | Class with __enter__/__exit__ |
| Quick linear setup/teardown | @contextmanager |
| Dynamic number of resources | ExitStack |
| Ignore one expected exception | contextlib.suppress |
| Mistake | Symptom | Fix |
|---|---|---|
Returning True from __exit__ accidentally | Real exceptions disappear | Return False unless deliberate suppression |
Doing heavy logic in __enter__ without rollback | Partial setup leaks | Keep setup minimal and rollback-safe |
| Manual open/close in long functions | Leak risk on exceptions | Use with blocks |
Using suppress(Exception) | Silent hidden failures | Suppress specific known exceptions only |
Managing dynamic resources with nested with chains | Hard-to-read code | Use ExitStack |
with Timer():Elapsed time printed after block exitsFileNotFoundError while deleting optional cache file.optional cache pathDelete if exists, no crash if missingkey="APP_MODE", value="test"Variable restored after with blockpaths listAll files safely closedDatabase transaction contextDo not suppress by default; rollback then re-raiseBeginner:
"What guarantee does with provide?"
Setup runs via
__enter__, and teardown via__exit__runs even if exceptions happen.
"When should you use @contextmanager?"
For straightforward setup/yield/teardown flows where a full class is unnecessary.
Senior:
"When is ExitStack better than nested with blocks?"
When resources are dynamic or conditionally acquired at runtime.
"Why is exception suppression dangerous in context managers?"
It can hide real failures and create false-success execution paths. Suppress only well-understood, explicitly safe exceptions.
@contextmanager for concise linear resource control.ExitStack is the right tool for dynamic resource sets.Need guaranteed cleanup? -> with
Need reusable manager object? -> class-based context manager
Need concise setup/teardown? -> @contextmanager
Need dynamic resources? -> ExitStackWhat guarantee does a with block provide?
__exit__ returning False means exceptions are not suppressed.
Great for simple one-resource policies.
ExitStack centralizes teardown for many context-managed resources.