with statements

PEP 343 - The “with” statement

https://docs.python.org/3/library/stdtypes.html#context-manager-types

https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers

INTRODUCTION

Python’s 'with' statement supports the concept of a runtime context defined by a context manager. This is implemented using a pair of methods that allow user-defined classes to define a runtime context that is entered before the statement body is executed (__enter__) and exited when the statement ends (__exit__).

Typical uses of context managers include saving and restoring various kinds of global state, locking and unlocking resources, closing opened files, etc.

The 'with' statement allows common 'try + except + finally' usage patterns to be encapsulated for convenient reuse.


# Syntax.

with expression as target:   # 'as target' is optional
    statements

with A() as a, B() as b:
    statements

# is semantically equivalent to nested 'with' statements:

with A() as a:
    with B() as b:
        statements

with open('book.txt', 'r') as infile:   # infile will be closed
    text = infile.read()

with open('results.txt', 'w') as outfile:   # outfile will be closed
    outfile.write("message\n")

CONTEXT MANAGERS


class ContextManager:

    def __init__(self):   # optional
        pass

    def message(self, argument):
        print("processing {}".format(argument))

    def __enter__(self):
        print("start with")
        return self   # or something else;
        # the return value will be assigned to 'target'

    def __exit__(self, exception_type, exception_value, exception_traceback):
        if exception_type is None:   # (None, None, None) is supplied to __exit__
            print("normal exit")   # no exception
            # put cleanup code here
            return True
        else:   # the exception type, value, traceback are supplied to __exit__
            print("exception raised is {}".format(exception_type))
            # put cleanup code here
            return False    # exception is reraised
            # return True   # exception is suppressed

# Usage.

with ContextManager() as context:
    context.message("test 1")
    print("point reached")

with ContextManager() as context:
    context.message("test 2")
    raise TypeError
    print("point not reached")

print("after with statements")

# Results.
# start with
# processing test 1
# point reached
# normal exit
# start with
# processing test 2
# exception raised is <class 'TypeError'>