Expert Python Programming(Third Edition)
上QQ阅读APP看书,第一时间看更新

As a function – the contextlib module

Using classes seems to be the most flexible way to implement any protocol provided in the Python language, but may be too much boilerplate for many simple use cases. A module was added to the standard library to provide helpers that simplify the creation of custom context managers. The most useful part of it is the contextmanager decorator. It allows us to provide both __enter__ and __exit__ procedures of the context manager within a single function, separated by a yield statement (note that this makes the function a generator). The previous example, when written with this decorator like would look like the following:

from contextlib import contextmanager 
 
@contextmanager 
def context_illustration(): 
    print('entering context') 
 
    try: 
        yield 
    except Exception as e: 
        print('leaving context') 
        print(f'with an error ({e})') 
        # exception needs to be reraised 
        raise 
    else: 
        print('leaving context') 
        print('with no error') 

If any exception occurs, the function needs to re-raise it in order to pass it along. Note that the context_illustration module could have some arguments if needed. This small helper simplifies the normal class-based context manager API exactly like generators do with the class-based iterator API.

The four other helpers provided by this module are as follows:

  • closing(element): This returns the context manager that calls the element's close() method on exit. This is useful for classes that deal with streams and files.
  • supress(*exceptions): This suppresses any of the specified exceptions if they occur in the body of the with statement.
  • redirect_stdout(new_target) and redirect_stderr(new_target): These redirect the sys.stdout or sys.stderr output of any code within the block to another file or file-like object.

Let's take a look at the functional-style features of Python.