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?
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.
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.
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?
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 # <<
Related
I have nested exceptions handling in my code, when inner blocks do some stuff before re-raising the exception to upper layers.
Traceback always reports the line number that started the exception handling.
However, when running in UI debugger (Pydev/Eclipse) it stops on the outer exception block in some cases.
Consider the following code for example:
import sys
def f(a):
c=5/a
return c
def sub(d):
print("Entered sub(%d)" % d)
try:
print("Entered try #sub")
e = f(d)/(d-1)
return e
except:
print("Do some staff before re-raising the exception upwards")
raise
def main():
try:
print("Entered try #main")
d = int(sys.argv[1])
sub(d)
except:
print("Reached except block #main")
raise
if __name__ == '__main__':
main()
When running with argument = 0, the exception is caused at line#4 and the debugger stops on that line:
However, when running with argument = 1, the exception is caused at line#11 (as reported in the printed traceback) but the debugger stops at line#15.
Once the debugger stops at the incorrect location it is very difficult to watch internal variables and handle the error, especially when the code in the try block uses loops.
In Pydev->Manage Exceptions, I checked only the "suspend on uncaught exceptions".
There is a checkbox "Skip exceptions caught in same function" that seem related (because it seems like the debugger skipped the first exception in sub and stopped on "raise" statement which can be considered another exception in same function, although the documentation says that it should re-raise the same exception).
This checkbox is grayed out unless I first check "Suspend on caught exceptions*", but once enabling it the debugger gets stuck and does not stop anywhere...
Will appreciate your help.
-Moshe
This function is supposed to catch exceptions in the main execution. If there is an exception it should print out the error with log.error(traceback.print_exc()) and clean up with exit_main().
def main():
try:
exec_app()
except KeyboardInterrupt:
log.error('Error: Backup aborted by user.')
exit_main()
except Exception:
log.error('Error: An Exception was thrown.')
log.error("-" * 60)
log.error(traceback.print_exc())
log.error("-" * 60)
exit_main()
Unfortunately log.error(traceback.print_exc()) does only return None if there is an exception. How can I make traceback print the full error report in this case?
PS: I use python 3.4.
From its __doc__:
Shorthand for 'print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)'
That is, it isn't supposed to return anything, its job is to print. If you want the traceback as a string to be logged, use traceback.format_exc() instead.
I usually use traceback.print_exc() just for debugging. In your case, to log your exception you can simply do the following:
try:
# Your code that might raise exceptions
except SomeSpecificException as e:
# Do something (log the exception, rollback, etc)
except Exception as e:
log.error(e) # or log(e.message) if you want to log only the message and not all the error stack
def receiver():
print("Ready to receive")
# try:
while True:
n = (yield)
print("Got %s" % n)
# except GeneratorExit:
# print("Receiver done")
r = receiver()
r.next()
r.send(10)
r.close()
When I comment out the GeneratorExit, the close() does not generate traceback! Why? However with the except active, i'm able to catch the GeneratorExit. How is that possible?
If GeneratorExit is an exception, then it should behave like all other exceptions and propagate out which it is doing - hence my ability to catch it using an except. But then surely without the except the parent python interpreter should catch the traceback like what happens with all other exceptions. What magic's going on here?
See generator.close() documentation.
It is stated that StopIteration and GeneratorExit exceptions are used internally to dictate how the generator exits, and thus are the only exceptions that are not propagated to the caller.
So if you want to re-raise a GeneratorExit exception, you probably have to catch it and encapsulate it in another custom exception.
Say I have the following code:
try:
[...]
except:
raise Exception([...])
finally:
[code]
My question is: if the code in the try block raises an exception which is caught in except, is [code] from the finally clause executed since a new exception is raised in the except clause? And if so, when is it executed? Before the new exception is raised or after the new exception is propagated through the method stack?
an example is worth a 1000 words, why didn't you just try what you've written?
>>> def foo():
>>> try:
>>> print "2 try block"
>>> raise Exception("1")
>>> print "never printed"
>>> except:
>>> print "3 first except block"
>>> raise Exception("2")
>>> finally:
>>> print "4 finally block"
>>> print "end of function"
>>>
>>> try:
>>> print "1 before foo"
>>> foo()
>>> print "never printed too"
>>> except:
>>> print "5 outter except clause"
1 before foo
2 try block
3 first except block
4 finally block
5 outter except clause
Before the new exception is raised or after the new exception is propagated through the method stack?
so as you can tell from the example, the finally block is called after the except block it has been defined in (i.e. after leaving the try/except/finally block), but before getting to the outer try/except block.
Which is logical, you want the finally to always be triggered when you exit the try block however you exit it, so you can be sure that your code environment is coherent when executing code outside of the try statement (whether you're releasing resources, or resetting values, or rolling back/committing modifications...).
finally is executed no matter the try block succeeds or the except block is run due to exceptions!
even if your except block raises an exception the new exception will be handled by another try catch handler but after executing the finally block, instead of forming a recursive loop:
try:
try:
[...]
except:
raise Exception([...]) #this is line number xyz
finally:
[code]
except:
[...] #this code will be running after line number xyz
I have a loop that I want to terminate on KeyboardInterrupt:
while True:
try:
do_stuff()
except KeyboardInterrupt:
cleanup()
break
except Exception as e:
cleanup()
raise e
This works fine, but the dual cleanup() seems very unclean to me. I don't like duplicated code. I tried using a context manager instead, but that introduced a lot of unnecessary complexity and nearly doubled the file size.
Is there a cleaner way to express my intent?
The finally keyword is exactly what you are looking for. The doc on errors and exceptions explains its usage.
A finally clause is always executed before leaving the try statement, whether an exception has occurred or not
If the cleanup is only supposed to occur when leaving the loop, I suggest swapping the loop and the try :
try:
while True:
do_stuff()
except KeyboardInterrupt:
pass
finally:
cleanup()
You can use BaseException to catch both
try:
do_stuff():
except BaseException as e:
cleanup()
if isinstance(e, KeyboardInterruption):
break
raise e
Also, you can use only raise instead of raise e
Sounds like you want the finally clause:
while True:
try:
do_stuff()
except KeyboardInterrupt:
break
finally:
cleanup()
cleanup() will always be called, whether or not the exception is raised or caught.