Python Context Managers

PyATL

Doug Hellmann

try:finally

f = open('/etc/hosts', 'r', encoding='utf-8')
try:
    for line in f:
        if 'apu' in line:
            print(line)
finally:
    # always called
    f.close()
10.9.0.10	apu.hellfly.net apu

with

#!/usr/bin/env python3

with open('/etc/hosts', 'r', encoding='utf-8') as f:
    print(f, '\n')
    for line in f:
        if 'apu' in line:
            print(line)
<_io.TextIOWrapper name='/etc/hosts' mode='r' encoding='utf-8'> 

10.9.0.10	apu.hellfly.net apu

closing()

import contextlib

class MyClosable(object):
    def __init__(self):
        print('init')

    def open(self):
        print('open')
        return self

    def close(self):
        print('close')

with contextlib.closing(MyClosable().open()) as f:
    print('working with', f)
init
open
working with <__main__.MyClosable object at 0x1007b1c50>
close

Class API

class MyContext(object):
    def __init__(self):
        print('__init__()')

    def __enter__(self):
        print('__enter__() (acquire the resource)')
        return 5

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__() (release the resource)')
        return True

with MyContext() as c:
    print('Doing work in the context', c)
__init__()
__enter__() (acquire the resource)
Doing work in the context 5
__exit__() (release the resource)

Error Handling

class MyContext(object):

    def __enter__(self):
        return 5

    def __exit__(self, exc_type, exc_val, exc_tb):
        if issubclass(exc_type, RuntimeError):
            print('Handling exception:', exc_val)
            return True
        print('Unhandled Exception:', exc_val)

with MyContext() as c:
    raise RuntimeError('this is OK')
Handling exception: this is OK

Error Handling

class MyContext(object):

    def __enter__(self):
        return 5

    def __exit__(self, exc_type, exc_val, exc_tb):
        if issubclass(exc_type, RuntimeError):
            print('Handling exception:', exc_val)
            return True
        print('Unhandled Exception:', exc_val)

with MyContext() as c:
    raise ValueError('this causes failure')
Unhandled Exception: this causes failure
Traceback (most recent call last):
  File "code/errors2.py", line 13, in <module>
    raise ValueError('this causes failure')
ValueError: this causes failure

Generator

import contextlib

@contextlib.contextmanager
def my_context():
    print('entering my_context')
    try:
        yield 'something'
    except RuntimeError:
        print('handled error in context manager')
    print('exiting my_context')

with my_context() as msg:
    print('doing work in the context "%s"' % msg)
    raise RuntimeError('hi again')
entering my_context
doing work in the context "something"
handled error in context manager
exiting my_context

Generator Error Handling

import contextlib

@contextlib.contextmanager
def my_context():
    print('entering my_context')
    try:
        yield 'something'
    except RuntimeError:
        print('handled error in context manager')
    print('exiting my_context')

with my_context() as msg:
    raise ValueError('fails again')
entering my_context
Traceback (most recent call last):
  File "code/generator2.py", line 13, in <module>
    raise ValueError('fails again')
ValueError: fails again

Resources

 dhellmann/presentation-python-context-managers
Standard Library: contextlib

 @doughellmann
https://doughellmann.com/presentations/python-context-managers

Creative Commons License  This work is licensed under a Creativle Commons Attribution 4.0 International License.