Intro Link to heading

pep-343 describes the context(pun intended) of context managers.

PEP 340, Anonymous Block Statements, combined many powerful ideas: using generators as block templates, adding exception handling and finalization to generators, and more

Basically pep 340 introduced the concept of anonymous blocks. which means that something

block EXPR1 as VAR1:
    BLOCK1

is the same as

itr = EXPR1  # The iterator
ret = False  # True if a return statement is active
val = None   # Return value, if ret == True
exc = None   # sys.exc_info() tuple if an exception is active
while True:
    try:
        if exc:
            ext = getattr(itr, "__exit__", None)
            if ext is not None:
                VAR1 = ext(*exc)   # May re-raise *exc
            else:
                raise exc[0], exc[1], exc[2]
        else:
            VAR1 = itr.next()  # May raise StopIteration

but pep-343 won with statement with

Specification Link to heading

the following context mangr expression

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)

The pepe defines EXPR and VAR as

Here, ‘with’ and ‘as’ are new keywords; EXPR is an arbitrary expression (but not an expression-list) and VAR is a single assignment target.

Examples and application Link to heading

The main application for context manager is resource management like file or lock. And handle exceptions inside BLOCK. So, even exception happens, the cleanup code will be executed.

context manager for file Link to heading

with open("t.log") as f:
	lines = f.readlines()

user defined context manager Link to heading

ie the context manager data model requires two dunder methods

  • enter
  • exit
class File():
    def __init__(self):
        print("__init__")
    def __enter__(self):
        print("__enter__")
    def __exit__(self, type_,  value, traceback):
        print("__exit__")
        print("type", type_)
        print("value", value)
        print("traceback", traceback)



with File() as file:
    print("something")

generates the following logs

__init__
__enter__
something
__exit__
type None
value None
traceback None

and when exception is raised the __exit__ is called with excep information and stacktrace

with File() as file:
    print("something")
    raise ValueError
__enter__
something
__exit__
type <class 'ValueError'>
value 
traceback <traceback object at 0x7f18a06b0500>
4
Traceback (most recent call last):
  File "cntx-mngr.py", line 23, in <module>
    raise ValueError
ValueError

Generator based context manager Link to heading

doc describes it best:

This function is a decorator that can be used to define a factory function for with statement context managers, without needing to create a class or separate enter() and exit() methods.

from contextlib import contextmanager

@contextmanager
def managed_resource(*args, **kwds):
    # Code to acquire resource, e.g.:
    resource = acquire_resource(*args, **kwds)
    try:
        yield resource
    finally:
        # Code to release resource, e.g.:
        release_resource(resource)

>>> with managed_resource(timeout=3600) as resource:
...     # Resource is released at the end of this block,
...     # even if code in the block raises an exception