7

What are my options when trying to ignore exceptions raised during the execution of a context manager? As is, the following simple context manager just propagates the results causing it to end execution unless caught:

class contextMan:    
    def __enter__(self):
        print("Entering Context")

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting Context")

if __name__ == "__main__":
    with contextMan():
        raise IndexError

I can of course surroung it in a try -- except clause but that seems like a rather tedious way to do it. Surely, there are better options to tackle this.

user2357112
  • 260,549
  • 28
  • 431
  • 505
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253

1 Answers1

9

Besides wrapping it in a try statement, there are two other ways you can go about suppressing a given exception in a context manager:

  1. Using an if condition that returns True if a given exception should be suppressed. This is based on the documentation which specifies that a True value suppresses the exception.

    This will work and on Python 2.x and on Python 3.x versions.

  2. Using contextlib.suppress with the given exception name(s) as a parameter.

    This only works for versions > 3.3


The first option is an option worth noting due to the fact that it will work on most Pythons, so portability points. It can simply be done by initializing the context manager with the needed exception and, on __exit__, adding an if clause which allows only given exceptions to propagate:

class contextMan:    
    def __init__(self, exception):
        self.exception = exception

    def __enter__(self):
        print("Entering Context")

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting Context")
        return isinstance(exc_value, self.exception)

if __name__ == "__main__":
    with contextMan(IndexError):
        raise IndexError

This will now suppress any instances of IndexError or any instance of a subclass of IndexError resulting in output of:

Entering Context
Exiting Context

The downside to this approach is that you're adding an extra attribute to each instance and essentially combining two different logical tasks in a single object instead of segregating them.


The second option is more robust, explicit and general. It was created specifically for these sort of scenarios. It is also a context manager and, as such, can generally be used in any case where specific exceptions should get suppressed.

It's call signature is of the form:

 contextlib.suppress(*exceptions) 

Where *exceptions is a tuple of exceptions to be suppressed. Keeping the original context manager as is, we can now create a nested context manager which also suppresses specific exceptions:

class contextMan:    
    def __enter__(self):
        print("Entering Context")

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting Context")

if __name__ == "__main__":
    with contextlib.suppress("IndexError")
        with contextMan():
            raise IndexError

Which could alternatively be written on the same line but that results in a rather lengthy statement. The obvious downside to this is that it introduces another statement each time you use the context manager.

Even though there are two options, there should be one-- and preferably only one --obvious way to do it and I believe the second one is the obvious way (at least for recent versions).

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253