python

Break statement in finally block swallows exception


Consider:

def raiseMe( text="Test error" ):
    raise Exception( text )

def break_in_finally_test():
    for i in range(5):
        if i==2:
            try:
                raiseMe()
            except:
                raise
            else:
                print "succeeded!"
            finally:
                print "testing this!"
                break

if __name__=='__main__':
    break_in_finally_test()

I expected to see Exception( "Test error" ) to be raised, but instead only "testing this" is printed. The intention, of course, was to call raiseMe() only once, no matter if we succeed or not - but if it raises an exception, I would have wanted to see that!

Why does break swallow the exception that I explicitly raise?


Solution

  • UPDATE: from Python3.14 onwards, using break, continue or return in a finally clause will trigger a SyntaxWarning (PEP765)

    From https://docs.python.org/2.7/reference/compound_stmts.html#finally:

    If finally is present, it specifies a ‘cleanup’ handler. The try clause is
    executed, including any except and else clauses. If an exception occurs in
    any of the clauses and is not handled, the exception is temporarily saved.
    The finally clause is executed. If there is a saved exception, it is
    re-raised at the end of the finally clause. If the finally clause raises
    another exception or executes a return or break statement, the saved
    exception is discarded

    This also reflects the behaviour expected from the try...finally statement before PEP341:

    This is how a try except finally block looked like pre PEP341:

    try:
        try:
            raiseMe()
        except:
            raise
    finally:
        #here is where cleanup is supposed to happen before raising error
        break
        #after finally code: raise error
    

    As the raising of errors never happens in the finally block it is never actually raised.

    To maintain backwards compatibility with Python<=2.4, it had to be done in this way.