Context managers were introduced in PEP 343.
They’re an incredibly useful construct for patterns of code that involve any sort of “cleanup” at the end of an execution of some code block.
In this article, I’m going to show the specification of context managers as laid out in PEP 343 in terms of actual code.
According to the spec, the following
with EXPR as VAR: BLOCK
mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) exc = True try: try: VAR = value # Only if "as VAR" is present BLOCK except: # The exceptional case is handled here exc = False if not exit(mgr, *sys.exc_info()): raise # The exception is swallowed if exit() returns true finally: # The normal and non-local-goto cases are handled here if exc: exit(mgr, None, None, None)
Here’s some code to demonstrate this equivalence.
First, this is a custom context manager I wrote for handling files opened for reading:
class MyFileContextManager(object): def __init__(self, name): self.name = name def __enter__(self): self.file = open(self.name, 'r') return self.file def __exit__(self, *exc_info): self.file.close()
with EXPR as VAR syntax:
with MyFileContextManager("http-request.py") as f: print(f.read())
And the “unpacked” form:
import sys mgr = MyFileContextManager('http-request.py') exit = type(mgr).__exit__ value = type(mgr).__enter__(mgr) exc = True try: try: f = value print(f.read()) except: exc = False if not exit(mgr, *sys.exc_info()): raise finally: if exc: exit(mgr, None, None, None)
Looking at actual code, we can see how much work is done behind the scenes when using
with. In a nutshell, it:
__enter__on the context manager object
- Executes the block of code nested inside the context manager
__exit__on the context manager object
If you look more closely, this implementation has many implications. For example, if you implement a custom context manager like I did and return a truthy value for
__exit__, the originating exception from the code block can get swallowed.
It’s not a ton of code, but the
with syntax is another one of those language constructs that make Python a joy to work with.