Skip to main content

Lib/contextlib.py (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Lib/contextlib.py

This annotation focuses on the higher-level contextlib utilities: ExitStack, contextmanager, and the helper managers. See also lib_contextlib_detail for suppress, closing, and AbstractContextManager.

Map

LinesSymbolRole
1-100AbstractContextManager, AbstractAsyncContextManagerABC bases
101-200contextmanagerGenerator-based context manager decorator
201-300asynccontextmanagerAsync variant
301-420closing, nullcontext, redirect_stdout, redirect_stderrSimple managers
421-550ExitStackDynamic context manager stack
551-650AsyncExitStackAsync variant of ExitStack

Reading

contextmanager protocol

contextmanager wraps a generator function. The generator yields exactly once; code before yield is the __enter__ body; code after is the __exit__ body.

# CPython: Lib/contextlib.py:127 _GeneratorContextManager.__enter__
def __enter__(self):
try:
return next(self.gen)
except StopIteration:
raise RuntimeError("generator didn't yield") from None

def __exit__(self, typ, value, traceback):
if typ is None:
try:
next(self.gen)
except StopIteration:
return False
else:
raise RuntimeError("generator didn't stop")
else:
...
try:
self.gen.throw(typ, value, traceback)
except StopIteration as exc:
return exc is not value
except RuntimeError as exc:
...

If an exception occurs, throw is called on the generator. If the generator yields again (doesn't re-raise), the exception is suppressed.

ExitStack

ExitStack accumulates context manager __exit__ callbacks and calls them in LIFO order when the stack itself is exited. This allows dynamic registration of arbitrary cleanup actions.

# CPython: Lib/contextlib.py:443 ExitStack.enter_context
def enter_context(self, cm):
_cm_type = type(cm)
_exit = _cm_type.__exit__
result = _cm_type.__enter__(cm)
self._push_cm_exit(cm, _exit)
return result

callback(fn, *args, **kwargs) registers a plain function as an exit action (called with no arguments).

nullcontext

# CPython: Lib/contextlib.py:688 nullcontext
class nullcontext(AbstractContextManager):
def __init__(self, enter_result=None):
self.enter_result = enter_result
def __enter__(self):
return self.enter_result
def __exit__(self, *excinfo):
pass

A no-op context manager useful as a default when a context manager is optional.

redirect_stdout

# CPython: Lib/contextlib.py:527 redirect_stdout
class redirect_stdout(_RedirectStream):
_stream = 'stdout'

Temporarily replaces sys.stdout with new_target. _RedirectStream.__enter__ saves the old stream and sets the new one; __exit__ restores the old one even if an exception occurred.

gopy notes

contextmanager is critical for gopy since it is the primary way users write context managers in Python 3 code. The generator-throw protocol requires vm.Frame.Throw. ExitStack requires correct exception propagation through multiple cleanup callbacks.