Search code examples
pythonpython-3.xexceptionwith-statementcontextmanager

python exception handling inside with block


Is the following code doing anything wrong with with statement and exception handling of python3? If no, what is the correct way of writing my expected output?

from contextlib import contextmanager

@contextmanager
def test():
    print("Hello")
    yield
    print("goodbye")

try:
    with test():
        print("inside test")
        raise KeyError
except KeyError:
    print("KeyError")
else:
    print("else")
finally:
    print("finally")

and the output is

Hello
inside test
KeyError
finally

and I expect the output is:

Hello
inside test
goodbye
KeyError
finally

which I believe other people write similarly in the hope that the file would be closed when an exception was raised during the processing of the file.

My python3 version is:

Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.version)
3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609]

Solution

  • The exception from within the block governed by the with statement is propagated to your generator context manager through generator.throw() as shown in PEP 343: "Generator Decorator", which raises the exception at the point where the generator was paused. In other words you should wrap the yield in a try/except or try/finally:

    @contextmanager
    def test():
        print("Hello")
        try:
            # The block of the with statement executes when the generator yields
            yield
    
        finally:
            print("goodbye")
    

    To quote the official documentation on the subject:

    ...If an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occurred. Thus, you can use a tryexceptfinally statement to trap the error (if any), or ensure that some cleanup takes place. If an exception is trapped merely in order to log it or to perform some action (rather than to suppress it entirely), the generator must reraise that exception. Otherwise the generator context manager will indicate to the with statement that the exception has been handled, and execution will resume with the statement immediately following the with statement.