Python traceback.print_exc() returns 'None' - python

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

Related

Stop program from running after try / except python

I made a program that takes values from treeview and calculate something when button is pressed. I put try / except statement inside that function.
def SumAll():
try:
#do something (calculate)
except ValueError:
Error=messagebox.showinfo("Enter proper values")
pass
The problem is, program keeps running when messagebox.showinfo appears, and it gives the ValueError.
How can I fix that, and how can I put more than one error exception (IndexError, etc)?
You can re-raise the exception, and if the exception reaches the top of the stack, the program will exit
try:
#do something (calculate)
except ValueError:
Error=messagebox.showinfo("Enter proper values")
raise
Or you can manually call sys.exit
import sys
try:
#do something (calculate)
except ValueError:
Error=messagebox.showinfo("Enter proper values")
sys.exit(1)
To catch more than on in the same handler, you can do something like
try:
#do something (calculate)
except (IndexError, ValueError):
Error=messagebox.showinfo("Enter proper values")
raise
or if you want different handlers you can have
try:
#do something (calculate)
except IndexError:
Error=messagebox.showinfo("Some message")
raise
except ValueError:
Error=messagebox.showinfo("Enter proper values")
raise
You can catch multiples by:
try:
k = input().split()[2]
i = int(k[0])
except (IndexError, ValueError) as e:
print(e) # list index error: 'Hello World', Value error: 'Hello World Tata'
else:
print("no error")
this will guard against inputs that split to less then 3 items '5 a' as well as against int conversion errors: 'Hello World Is A Cool Thing' (`'Is'`` is not an integer).
Use How to debug small programs (#1) to debug through your real program, the error you get above this except is caught. You probably get a resulting error somewhere else because somethings not right with the return of your function.

Catch all exceptions except user abort

I have a script that catches all exceptions, which works great unless I want to abort the script manually (with control + c). In this case the abort command appears to be caught by the exception instead of quitting.
Is there a way to exclude this type of error from the exception? For example something as follows:
try:
do_thing()
except UserAbort:
break
except Exception as e:
print(e)
continue
You could just force to exit the program whenever the exception happens:
import sys
# ...
try:
do_thing()
except UserAbort:
break
except KeyboardInterrupt:
sys.exit()
pass
except Exception as e:
print(e)
continue

GeneratorExit, close() does not cause a traceback/exception - how?

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.

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?
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 # <<

in python , why is there a difference in exception when signal handling is done before and after "try except"

I recently started with python . I was playing with handling the keyboard interrupt , when I came across this behavior
import signal,sys
def handleInt(sign,no):
print "interrupted"
signal.signal(signal.SIGINT,handleInt) # exception raised is IOError
try:
sys.stdin.read(1)
except IOError:
print "io interrupt"
but if I change the signal handling to after the try-except
import signal,sys
def handleInt(sign,no):
print "interrupted"
try:
sys.stdin.read(1)
except KeyboardInterrupt:
print "keyboard interrupt"
signal.signal(signal.SIGINT,handleInt) # exception raised is KeyboardInterrupt
When I press ctrl+c , there is a difference in the exception in the two cases .So why is this behavior ?
Python has its own built-in singal handler for SIGINT. This handler simply raises KeyboardInterrupt. In your first code, you replaced the built-in handler with the new handler hence you see this output:
$python test_exc.py
^Cinterrupted
Note that io interrupted is not printed, since no exception was raised. In fact modifying the code to:
import signal,sys
def handleInt(sign,no):
print "interrupted"
signal.signal(signal.SIGINT, handleInt) # exception raised is IOError
try:
sys.stdin.read(1)
except IOError:
print "io interrupt"
else:
# else is executed only if no exception was raised
print "done"
You get:
$python test_exc.py
^Cinterrupted
done
Note that hitting Ctrl+C does not block the call to sys.stdin.read(1) hence you still have to press some key to let the program continue. Raising an exception inside the signal handler will raise it as if the call to sys.stdin.read(1) produced it:
import signal,sys
def handleInt(sign,no):
print "interrupted"
raise OSError
signal.signal(signal.SIGINT, handleInt) # exception raised is IOError
try:
sys.stdin.read(1)
except IOError:
print "io interrupt"
else:
# else is executed only if no exception was raised
print "done"
Sample run:
$python test_exc.py
^Cinterrupted
Traceback (most recent call last):
File "test_exc.py", line 10, in <module>
sys.stdin.read(1)
File "test_exc.py", line 5, in handleInt
raise OSError
OSError
Note: you can access the default signal handler via signal.default_int_handler.
When you try and register the signal after the blocking call to sys.stdin.read, you never actually get there.
So you get an exception when you hit Ctrl-C which raises a KeyboardInterrupt breaks out of the read and is caught by the try.
When you actually register the signal handler in the first example something slightly different is happening. The interrupt is being handled by your handleInt code.

Categories