Capture Nested Exceptions in Python - python

I have set of Python scripts which are calling functions in a nested way. For each of these functions I have a try, except statement to capture every exception and print them. I would like to send an e-mail alert containing the complete sequence of exceptions encountered during the execution. Example:
import sys
def SendAlert(ErrorMessage):
try:
#send email alert with error message
#[...]
except:
print(str(sys.exc_info()))
return(sys.exc_info())
def ParentFunction():
try:
#call ChildFunction
ChildResult = ChildFunction()
#do stuff with ChildResult
#[...]
return ParentResult
except:
ErrorMessage = str(sys.exc_info())
print(ErrorMessage)
SendAlert(ErrorMessage)
def ChildFunction():
try:
#do stuff
#[...]
return ChildResult
except:
print(str(sys.exc_info()))
return(sys.exc_info())
#main
if __name__ == '__main__':
Result = ParentFunction()
The code above would behave as follow in case of error in ChildFunction which is the most nested function:
ChildFunction encounters an exception it will print it and return
the error message to ParentFunction
ParentFunction will fail because ChildResult contains an error message and not a valid value
ParentFunction will trigger and exception and send its own error message in the e-mail alert
In addition to the error message from ParentFunction, I would like the e-mail alert to contain the error message from ChildFunction. Note that I would like to avoid passing ChildResult variable to SendAlert function in the except statement of ParentFunction because in real life my program has a lot of nested functions and it would mean passing the result variable of every single function into the except statement.
How would you achieve this? Is there a way to access the complete sequence of errors triggered by the whole program?
Thanks

you don't need to return exceptions obtained with sys.exc_info: we can just re-raise it
try:
# do stuff
# FIXME: we should avoid catching too broad exception
except Exception as err:
# do stuff with exception
raise err
so your example may look like
def SendAlert(ErrorMessage):
try:
# send email alert with error message
# [...]
pass
# what kind of exceptions may occur while sending email?
except Exception as err:
print(err)
raise err
def ParentFunction():
try:
# call ChildFunction
ChildResult = ChildFunction()
ParentResult = ChildResult
# do stuff with ChildResult
# [...]
return ParentResult
# FIXME: we should avoid catching too broad exception
except Exception as err:
ErrorMessage = str(err)
# why do we need to print again?
print(ErrorMessage)
SendAlert(ErrorMessage)
def ChildFunction():
try:
ChildResult = 0
# do stuff
# [...]
# let's raise `ZeroDivisionError`
ChildResult /= 0
return ChildResult
# FIXME: we should avoid catching too broad exception
except Exception as err:
print(err)
raise err
# main
if __name__ == '__main__':
Result = ParentFunction()
Further improvements
For printing full error traceback we can use logging module like
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def SendAlert(ErrorMessage):
try:
# send email alert with error message
# [...]
pass
# what kind of exceptions may occur while sending email?
except Exception as err:
logger.exception('Error while sending email')
# we're not receiving values from this function
raise err
def ParentFunction():
try:
# call ChildFunction
ChildResult = ChildFunction()
ParentResult = ChildResult
# do stuff with ChildResult
# [...]
return ParentResult
# FIXME: we should avoid catching too broad exception
except Exception as err:
# this will log full error traceback
# including `ChildFunction`
logger.exception('Error in ParentFunction')
ErrorMessage = str(err)
SendAlert(ErrorMessage)
def ChildFunction():
ChildResult = 0
# do stuff
# [...]
# e. g. let's raise `ZeroDivisionError`
ChildResult /= 0
return ChildResult
# main
if __name__ == '__main__':
Result = ParentFunction()
And it is just the tip of the iceberg, logging is awesome and you definitely should use it.
Further reading
Logging HOWTO,
SMTPHandler for sending errors on email via SMTP

You can, also, create a custom exception that can take a descriptive error message and return it.
Here is, a trivial example that you can modify and implement it into your code untill it fills your needs:
class MyCustomError(Exception):
def __init__(self, err):
Exception.__init__(self)
self.error = err
def __str__(self):
return "%r" % self.error
a = 1
try:
if a != 0:
raise MyCustomError("This is an Error!")
except MyCustomError as err:
print(err)
Output:
'This is an Error!'

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

Python traceback.print_exc() returns 'None'

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

nested try except in python

Try:
#some statement
Try:
#some statement
Except:
#statement1
Raise exception()
#statement2
Except: #some statement
Can I pass the control like the above code in python., will the inner except pass the control to the outer except and will the #statement2 be executed?
This code will answer your question:
#!/usr/bin/env python
import sys
try:
try:
raise Exception("first exception")
except Exception as e:
print e.message
raise Exception("second exception")
print "second statement" # never printed - 'dead code'
except Exception as e:
print e.message
Both except blocks are executed but the statement after raising the second exception is not.
Generally you should know that once an exception is raised, nothing executes until it is caught by an except block that is relevant to this exception or any superclass of it.

How to handle python signal exception?

def signal_handler(signum, frame):
raise Exception("Timeout")
def my_function():
signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(1)
try:
call_long_function()
except Exception as e: # Doesn't work
print("Hello, Timeout!")
break
signal.alarm(0)
How to correctly handle signal exception? Also I tried to handle Timeout exception, it doesn't work too.
It just prints "Error: Timeout" to the console and after it program will be closed.
Thanks in advance.
You're probably better off subclassing Exception into your own Exception class:
class TimeOutException(Exception):
def __init__(self, message, errors):
super(TimeOutException, self).__init__(message)
self.errors = errors
Then you can do:
def signal_handler(signum, frame):
raise TimeOutException("Timeout!")
Then, in the other function:
try:
call_long_function():
except TimeOutException:
print "Got a timeout!"
break
It is better to create a specific exception class for the timeout exception so you can differentiate between it and other exceptions.
class TimeoutException(Exception):
def __init__(self, *args, **kwargs):
pass
Define an alarm signal in your function, and set a timer.
Then wrap your time limited code with a try/except.
In this example i used two cases - one for the timeout exception class - one for any other sort of exception assuming the code can fail for more than one reason.
def my_function():
signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(1) # Time Limit
try:
time.sleep(120) # Simulation of code exceeding time limit
except TimeoutException:
print "Time out"
except:
print "Some other exception"
def signal_handler(signum, frame):
raise TimeoutException()
try using the following snippet:
try:
//CODE
except Exception,e:
//handle the exception.
The whole point is not to use the expression Exception as e

Categories