Python Context Manager Protocol

A context manager implements setup and cleanup around a `with` block through `__enter__()` and `__exit__()`. Its exit hook runs during normal completion and exception unwinding, and can choose whether an exception propagates.

A context manager is an object that defines how a `with` block begins and ends. The protocol consists of `__enter__()` and `__exit__(exc_type, exc_value, traceback)`.

Python evaluates the manager expression, calls `__enter__()`, and binds its return value to the name after `as`, if present. The block then runs. When it finishes, Python calls `__exit__()` whether the block completed normally or raised an exception.

If the block raises an exception, `__exit__()` receives its type, value, and traceback. Returning a truthy value suppresses that exception; returning a false value allows it to propagate. If the block completes normally, the three exception arguments are `None`.

Context managers are useful for resources with a clear lifetime, such as files, locks, and database transactions. They centralize cleanup so callers do not have to repeat a `try/finally` pattern. The standard library's `contextlib` provides helpers, including `contextmanager`, for defining managers with less boilerplate.

Key points

Interview questions

What is a context manager in Python?

A context manager defines behavior for entering and leaving a `with` block. Its `__enter__()` method performs setup and returns the value optionally bound by `as`. Its `__exit__()` method handles cleanup and is called even if the block raises. It can suppress an exception by returning a truthy value.

What is the execution order of a `with` statement?

Python calls `__enter__()` before the suite and uses its return value for the optional `as` target. After the suite, it calls `__exit__()` with three `None` values if the suite completed normally. If the suite raised, it passes the exception type, exception instance, and traceback. A truthy result suppresses the exception; a false result lets it propagate.

How should `__exit__()` decide whether to suppress an exception?

Return `False` or another false value from `__exit__()` when the exception should continue outward. Return a truthy value only when the manager deliberately handles the exception and the caller should continue as if it were suppressed. Suppression is limited to the exception from the managed suite; exceptions raised by `__exit__()` itself are not suppressed by that same call. In most resource managers, cleanup should happen and the original exception should propagate.

How does the protocol help manage resource lifetimes safely?

`__enter__()` performs setup and returns the object or resource that the block needs. `__exit__()` releases or finalizes it, regardless of how the block ends. If setup in `__enter__()` fails, the suite was never entered and Python does not call that manager's `__exit__()`. Therefore, `__enter__()` should avoid leaving partially acquired resources behind if it can fail partway through.

How does `contextlib.contextmanager` implement a context manager?

`contextlib.contextmanager` lets a generator describe a context manager. Code before `yield` runs on entry, the yielded value becomes the `as` target, and code after `yield` runs during exit. If the suite raises, that exception is thrown at the `yield` point, so a `try/finally` is a common way to guarantee cleanup. To suppress an exception, the generator must handle it rather than re-raise it.