Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Break statement in finally block swallows exception

Tags:

python

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?

like image 296
TimO Avatar asked Feb 19 '16 12:02

TimO


4 Answers

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.

like image 135
M.T Avatar answered Nov 01 '22 19:11

M.T


From the docs Error Handling Docs:

A finally clause is always executed before leaving the try statement, whether an exception has occurred or not.

Your exception never gets raised because you break before the try statement gets fully evaluated.

like image 37
meltdown90 Avatar answered Nov 01 '22 20:11

meltdown90


I think upon reflection that it is due to the fact that break actually raises a StopIteration to "break" out of the for-loop. This is really not very intuitive and not particularly well documented (not mentioned on 1, for example). Could maybe someone confirm/explain it better?

like image 1
TimO Avatar answered Nov 01 '22 19:11

TimO


Have the following code structure:

def func():
    try: 
        driver.do("something")
    except TimeoutException:
        pass
    finally:
        result = driver.do("something else")
        return result

Got the exception by pylint:

return statement in finally block may swallow exception (lost-exception)

Solution was to put return out of the finally statement:

def func():
    try: 
        driver.do("something")
    except TimeoutException:
        pass
    finally:
        result = driver.do("something else")
    return result # << 
like image 1
klapshin Avatar answered Nov 01 '22 21:11

klapshin