Context Manager Specification With Python Code

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 syntax:

with EXPR as VAR:
    BLOCK

Translates to:

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()

Using the 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:

  1. Invokes __enter__ on the context manager object
  2. Executes the block of code nested inside the context manager with
  3. Invokes __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.