I want to check for an exception in Python unittest, with the following requirements:
Needs to be reported as a failure, NOT an error
Must not swallow the original exception
I've seen lots of solutions of the form:
try:
something()
except:
self.fail("It failed")
Unfortunately these solutions swallow the original exception. Any way to retain the original exception?
I ended up using a variant of Pierre GM's answer:
try:
something()
except:
self.fail("Failed with %s" % traceback.format_exc())
As suggested, you could use the context of a generic exception:
except Exception, error:
self.fail("Failed with %s" % error)
You can also retrieve the information relative to the exception through sys.exc_info()
try:
1./0
except:
(etype, evalue, etrace) = sys.exc_info()
self.fail("Failed with %s" % evalue)
The tuple (etype, evalue, etrace) is here (<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('float division',), <traceback object at 0x7f6f2c02fa70>)
Related
Here is a quick example of what I mean -
try:
something_bad
# This works -
except IndexError as I:
write_to_debug_file('there was an %s' % I)
# How do I do this? -
except as O:
write_to_debug_file('there was an %s' % O)
What is the correct syntax for the second exception?
Thanks in advance :)
except Exception as exc:
Exception is the base class for all "built-in, non-system-exiting exceptions" and should be the base class for user-defined ones as well. except Exception will therefore catch everything except for the few that do not subclass Exception, such as SystemExit, GeneratorExit, KeyboardInterrupt.
You don't have to specify the error type. Just use sys.exc_info() to read out the last exception:
import sys
try:
foobar
except:
print "Unexpected error of type", sys.exc_info()[0].__name__
print "Error message:", sys.exc_info()[1]
Reference: https://docs.python.org/2/tutorial/errors.html#handling-exceptions
As already pointed by Jason, you can use except Exception as O or except BaseException as O if you want to catch all exceptions, including KeyboardInterrupt.
If you need exception's name, you can use name = O.__class__.__name__ or name = type(O).__name__.
Hope this helps
How to handle all but one exception?
try:
something
except <any Exception except for a NoChildException>:
# handling
Something like this, except without destroying the original traceback:
try:
something
except NoChildException:
raise NoChildException
except Exception:
# handling
The answer is to simply do a bare raise:
try:
...
except NoChildException:
# optionally, do some stuff here and then ...
raise
except Exception:
# handling
This will re-raise the last thrown exception, with original stack trace intact (even if it's been handled!).
New to Python ... but is not this a viable answer?
I use it and apparently works.... and is linear.
try:
something
except NoChildException:
assert True
except Exception:
# handling
E.g., I use this to get rid of (in certain situation useless) return exception FileExistsError from os.mkdir.
That is my code is:
try:
os.mkdir(dbFileDir, mode=0o700)
except FileExistsError:
assert True
and I simply accept as an abort to execution the fact that the dir is not somehow accessible.
I'd offer this as an improvement on the accepted answer.
try:
dosomestuff()
except MySpecialException:
ttype, value, traceback = sys.exc_info()
raise ttype, value, traceback
except Exception as e:
mse = convert_to_myspecialexception_with_local_context(e, context)
raise mse
This approach improves on the accepted answer by maintaining the original stacktrace when MySpecialException is caught, so when your top-level exception handler logs the exception you'll get a traceback that points to where the original exception was thrown.
You can do type checking on exceptions! Simply write
try:
...
except Exception as e:
if type(e) == NoChildException:
raise
It still includes the original stack trace.
I found a context in which catching all errors but one is not a bad thing, namely unit testing.
If I have a method:
def my_method():
try:
something()
except IOError, e:
handle_it()
Then it could plausibly have a unit test that looks like:
def test_my_method():
try:
my_module.my_method()
except IOError, e:
print "shouldn't see this error message"
assert False
except Exception, e:
print "some other error message"
assert False
assert True
Because you have now detected that my_method just threw an unexpected exception.
Say I have some code like this:
try:
try:
raise Exception("in the try")
finally:
raise Exception("in the finally")
except Exception, e:
print "try block failed: %s" % (e,)
The output is:
try block failed: in the finally
From the point of that print statement, is there any way to access the exception raised in the try, or has it disappeared forever?
NOTE: I don't have a use case in mind; this is just curiosity.
I can't find any information about whether this has been backported and don't have a Py2 installation handy, but in Python 3, e has an attribute called e.__context__, so that:
try:
try:
raise Exception("in the try")
finally:
raise Exception("in the finally")
except Exception as e:
print(repr(e.__context__))
gives:
Exception('in the try',)
According to PEP 3314, before __context__ was added, information about the original exception was unavailable.
try:
try:
raise Exception("in the try")
except Exception, e:
print "try block failed"
finally:
raise Exception("in the finally")
except Exception, e:
print "finally block failed: %s" % (e,)
However, It would be a good idea to avoid having code that is likely to throw an exception in the finally block - usually you just use it to do cleanup etc. anyway.
I'm adding logging to some python code that deals with exceptions, in the example below what's the correct syntax for wanting to log exception details (e.g. via logger.exception()) when a TypeError or AttributeError occurs?
try:
...
except (TypeError, AttributeError):
# want to do a logger.exception(x) here but not sure what to use for x
...
raise CustomError("Unable to parse column status)
exception(...) is just a convenience method which takes a message just like the other methods:
def exception(self, msg, *args):
"""
Convenience method for logging an ERROR with exception information.
"""
self.error(msg, exc_info=1, *args)
So you would just use it like
logger.exception("Some error message")
and the logging handler will automatically add the exception information from the current exception. Only use this in an exception handler (i.e. in a except: block)!
If you want the exception details, you need to bind the exception itself to a local variable, like this:
except (TypeError, AttributeError), e:
# e is the Exception object
logger.exception(e)
If you need to do different things based on the type of the exception, then you can catch them separately:
except TypeError, e:
logger.exception('There was a Type Error; details are %s' % e)
# Do something, or raise another exception
except AttributeError, e:
logger.exception('There was an Attribute Error; details are %s' % e)
# Do something, or raise another exception
And if you need more information about the context of the exception itself, look into the sys.exc_info() function; it can get you the traceback, and the details about exactly where the exception occurred.
import ftplib
import urllib2
import os
import logging
logger = logging.getLogger('ftpuploader')
hdlr = logging.FileHandler('ftplog.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
FTPADDR = "some ftp address"
def upload_to_ftp(con, filepath):
try:
f = open(filepath,'rb') # file to send
con.storbinary('STOR '+ filepath, f) # Send the file
f.close() # Close file and FTP
logger.info('File successfully uploaded to '+ FTPADDR)
except, e:
logger.error('Failed to upload to ftp: '+ str(e))
This doesn't seem to work, I get syntax error, what is the proper way of doing this for logging all kind of exceptions to a file
You have to define which type of exception you want to catch. So write except Exception, e: instead of except, e: for a general exception (that will be logged anyway).
Other possibility is to write your whole try/except code this way:
try:
with open(filepath,'rb') as f:
con.storbinary('STOR '+ filepath, f)
logger.info('File successfully uploaded to '+ FTPADDR)
except Exception, e: # work on python 2.x
logger.error('Failed to upload to ftp: '+ str(e))
in Python 3.x and modern versions of Python 2.x use except Exception as e instead of except Exception, e:
try:
with open(filepath,'rb') as f:
con.storbinary('STOR '+ filepath, f)
logger.info('File successfully uploaded to '+ FTPADDR)
except Exception as e: # work on python 3.x
logger.error('Failed to upload to ftp: '+ str(e))
The syntax is no longer supported in python 3. Use the following instead.
try:
do_something()
except BaseException as e:
logger.error('Failed to do something: ' + str(e))
If you want the error class, error message, and stack trace, use sys.exc_info().
Minimal working code with some formatting:
import sys
import traceback
try:
ans = 1/0
except BaseException as ex:
# Get current system exception
ex_type, ex_value, ex_traceback = sys.exc_info()
# Extract unformatter stack traces as tuples
trace_back = traceback.extract_tb(ex_traceback)
# Format stacktrace
stack_trace = list()
for trace in trace_back:
stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3]))
print("Exception type : %s " % ex_type.__name__)
print("Exception message : %s" %ex_value)
print("Stack trace : %s" %stack_trace)
Which gives the following output:
Exception type : ZeroDivisionError
Exception message : division by zero
Stack trace : ['File : .\\test.py , Line : 5, Func.Name : <module>, Message : ans = 1/0']
The function sys.exc_info() gives you details about the most recent exception. It returns a tuple of (type, value, traceback).
traceback is an instance of traceback object. You can format the trace with the methods provided. More can be found in the traceback documentation .
There are some cases where you can use the e.message or e.messages.. But it does not work in all cases. Anyway the more safe is to use the str(e)
try:
...
except Exception as e:
print(e.message)
Updating this to something simpler for logger (works for both python 2 and 3). You do not need traceback module.
import logging
logger = logging.Logger('catch_all')
def catchEverythingInLog():
try:
... do something ...
except Exception as e:
logger.error(e, exc_info=True)
... exception handling ...
This is now the old way (though still works):
import sys, traceback
def catchEverything():
try:
... some operation(s) ...
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
... exception handling ...
exc_value is the error message.
You can use logger.exception("msg") for logging exception with traceback:
try:
#your code
except Exception as e:
logger.exception('Failed: ' + str(e))
Using str(e) or repr(e) to represent the exception, you won't get the actual stack trace, so it is not helpful to find where the exception is.
After reading other answers and the logging package doc, the following two ways works great to print the actual stack trace for easier debugging:
use logger.debug() with parameter exc_info
try:
# my code
except SomeError as e:
logger.debug(e, exc_info=True)
use logger.exception()
or we can directly use logger.exception() to print the exception.
try:
# my code
except SomeError as e:
logger.exception(e)
After python 3.6, you can use formatted string literal. It's neat! (https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498)
try
...
except Exception as e:
logger.error(f"Failed to upload to ftp: {e}")
You can try specifying the BaseException type explicitly. However, this will only catch derivatives of BaseException. While this includes all implementation-provided exceptions, it is also possibly to raise arbitrary old-style classes.
try:
do_something()
except BaseException, e:
logger.error('Failed to do something: ' + str(e))
If you want to see the original error message, (file & line number)
import traceback
try:
print(3/0)
except Exception as e:
traceback.print_exc()
This will show you the same error message as if you didn't use try-except.
for the future strugglers,
in python 3.8.2(and maybe a few versions before that), the syntax is
except Attribute as e:
print(e)
Use str(ex) to print execption
try:
#your code
except ex:
print(str(ex))
In Python 3, str(ex) gives us the error message. You could use repr(ex) to get the full text, including the name of the exception raised.
arr = ["a", "b", "c"]
try:
print(arr[5])
except IndexError as ex:
print(repr(ex)) # IndexError: list index out of range
print(str(ex)) # list index out of range
There is also a way to get the raw values passed to the exception class without having to change the content type.
For e.g I raise type codes with error messages in one of my frameworks.
try:
# TODO: Your exceptional code here
raise Exception((1, "Your code wants the program to exit"))
except Exception as e:
print("Exception Type:", e.args[0][0], "Message:", e.args[0][1])
Output
Exception Type: 1 Message: 'Your code wants the program to exit'
The easiest way to do this is available through the Polog library. Import it:
$ pip install polog
And use:
from polog import log, config, file_writer
config.add_handlers(file_writer('file.log'))
with log('message').suppress():
do_something()
Note how much less space the code has taken up vertically: only 2 lines.