with calls enter and guarantees exit for cleanup, including on exceptions
The with statement calls type(cm).enter(cm) and binds the return value to the as target, then always calls exit on the way out, whether the block completed, raised, or was exited early. exit receives the exception type, value, and traceback. Returning True suppresses the exception; returning anything falsy lets it propagate. That is the whole protocol, and it is why with is strictly better than try/finally for resource cleanup: it is shorter, harder to misuse, and composes. The contextlib module provides contextmanager for generator-based context managers and ExitStack for combining many of them dynamically.
enter can return self, a resource, or None. Choose deliberately so the as target is useful.
exit must not swallow exceptions by accident. Returning True suppresses, returning None propagates.
contextlib.contextmanager turns a generator with one yield into a context manager; the code after yield runs as exit.
contextlib.ExitStack is the right tool when the number of resources is dynamic or conditionally acquired.
Trade-off: exit cannot easily distinguish a normal exit from an exception unless you inspect the arguments. Do not over-engineer that distinction.
Common mistake: doing work only in enter without a matching exit path, which leaks resources on exceptions.
Version note: the with statement landed in Python 2.5, contextlib in 2.5, and ExitStack in 3.3.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience