Skip to the content.

Context Manager Protocol

Recipes

from types import TracebackType
from typing import Type


class ContextManager:
    """A example of context manager."""

    def __init__(self, propogate=False, exit_raise_exception=False):
        self.propogate = propogate
        self.exit_raise_exception = exit_raise_exception

    def __enter__(self):
        """Enter the runtime context and return either this object
        or another object related to the runtime context.

        The value returned by this method is bound to the identifier
        in the `as` clause of `with` statements using this context manager.

        - An example of a context manager that returns itself is a `file` object.
        File objects return themselves from `__enter__()`
        to allow `open()` to be used as the context expression in a `with` statement.

        - An example of a context manager that returns a related object
        is the one returned by `decimal.localcontext()`.
        These managers set the active `decimal` context to a copy of
        the original `decimal` context and then return the copy.
        This allows changes to be made to the current `decimal` context
        in the body of the `with` statement without affecting code
        outside the `with` statement.
        """
        print('enter into runtime context')
        return self

    def __exit__(self,
                 exc_type: Type[BaseException] | None,
                 exc_val: Exception | None,
                 exc_tb: TracebackType) -> bool:
        """Exit the runtime context and return a Boolean flag
        indicating if any exception that occurred should be suppressed.
        `True` for suppressed.

        If an exception occurred while executing the body of the `with` statement,
        the arguments contain the exception type, value and traceback information.
        Otherwise, all three arguments are `None`.
        """
        print('exit from runtime context')
        if self.exit_raise_exception:
            raise TypeError
        if exc_type is not None:
            print(exc_type)
        return self.propogate

Case 1: No exception occurred while executing the body of the with statement:

>>> with ContextManager() as cm:
...     print('run')
...
enter into runtime context
run
exit from runtime context

Case 2: Exception(s) occurred while executing the body of the with statement, exception occurred NOT be suppressed by default:

>>> with ContextManager() as cm:
...     print('run')
...     raise KeyError
...
enter into runtime context
run
exit from runtime context
<class 'KeyError'>
KeyError

Case 3: Exception(s) occurred while executing the body of the with statement, exception occurred is suppressed:

>>> with ContextManager(propogate=True) as cm:
...     print('run')
...     raise KeyError
...
enter into runtime context
run
exit from runtime context
<class 'KeyError'>

Case 4 (not recommended): Exceptions that occur during execution of __exit__() method will replace any exception that occurred in the body of the with statement:

Warn: The exception passed in should never be reraised explicitly - instead, this method should return a false value to indicate that the method completed successfully and does not want to suppress the raised exception. This allows context management code to easily detect whether or not an __exit__() method has actually failed.

>>> with ContextManager(exit_raise_exception=True) as cm:
...     print('run')
...     raise KeyError
...
enter into runtime context
run
exit from runtime context
KeyError
During handling of the above exception, another exception occurred:
TypeError

References