Python3. How to correctly raise exception inside context manager to handle it out of with operator?

Viewed 500

I want to catch exception that was raised inside context manager. I created simple example to reproduce the problem.

So, my context manager:

class Test(object):
    def div(self, a, b):
        return a // b
    def __enter__(self):
        print('enter')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return self 

And class where I want to use it:

class Container(object):
    def test_exc(self, a, b):
        try:
            with Test() as test:
                try:
                    result = test.div(a, b)
                    print(a, '/', b, '=', result)
                    return result
                except Exception as e:
                    raise e
        except Exception as e:
            print(e)

Usage:

c = Container()
c.test_exc(5, 1)
c.test_exc(5, 0)

The output:

enter
5 / 1 = 5
exit
enter
exit

So, when exception raised (in example above it's ZeroDivisionError) it's not catched by parent try...catch. How to fix this ?

2 Answers

The error is the implemetation of your __exit__() function.

The documentation states:

returning a true value from this method will cause the with statement to suppress the exception and continue execution with the statement immediately following the with statement. Otherwise the exception continues propagating after this method has finished executing. Exceptions that occur during execution of this method will replace any exception that occurred in the body of the with statement.

just remove the

return self

from the __exit__() function. Having no return will return None which evaluates as False

I think that context managers can optionally pass on exceptions that they catch.

To stop exceptions, the __exit__ method should return True (and self might count as True)

To pass exceptions on, return False:

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return False

See this link to the PEP where some pseudo code shows how the exception may be swallowed.

Related