Context Managers in Python
Guarantee that setup is always paired with teardown, even when things go wrong
with statement drives it: __enter__ runs on the way in, and __exit__ is guaranteed to run on the way out — whether the block finishes normally, hits a return, or raises. It is the language-level answer to "acquire a resource, use it, and always release it".
- explain how
withmaps onto the__enter__/__exit__protocol, - write a robust class-based context manager, including correct exception handling in
__exit__, - build one far more cheaply with
@contextlib.contextmanagerand a generator, - reason about
__exit__'s return value and when it swallows exceptions, - compose several managers dynamically with
contextlib.ExitStack, - reach for
suppress,closingandnullcontextinstead of writing boilerplate.
1. The problem: cleanup that must not be skipped
Almost every non-trivial program acquires resources that must later be released: open files, network sockets, database connections, threading locks, temporary directories. The naive version leaks the moment anything raises:
f = open("data.txt")
process(f.read()) # if this raises, the next line never runs
f.close() # LEAK: the file is never closed
The historically correct fix is try/finally: the finally block runs no matter how the try block exits.
f = open("data.txt")
try:
process(f.read())
finally:
f.close() # ALWAYS runs, even on an exception
This works, but it is verbose, easy to forget, and the acquisition and release drift apart as the block grows. A context manager packages exactly this try/finally discipline into a reusable object so the caller writes one clean line.