Python unittest: how do I test the argument in an Exceptions? - python

I am testing for Exceptions using unittest, for example:
self.assertRaises(UnrecognizedAirportError, func, arg1, arg2)
and my code raises:
raise UnrecognizedAirportError('From')
Which works well.
How do I test that the argument in the exception is what I expect it to be?
I wish to somehow assert that capturedException.argument == 'From'.
I hope this is clear enough - thanks in advance!
Tal.

Like this.
>>> try:
... raise UnrecognizedAirportError("func","arg1","arg2")
... except UnrecognizedAirportError, e:
... print e.args
...
('func', 'arg1', 'arg2')
>>>
Your arguments are in args, if you simply subclass Exception.
See http://docs.python.org/library/exceptions.html#module-exceptions
If the exception class is derived from
the standard root class BaseException,
the associated value is present as the
exception instance’s args attribute.
Edit Bigger Example.
class TestSomeException( unittest.TestCase ):
def testRaiseWithArgs( self ):
try:
... Something that raises the exception ...
self.fail( "Didn't raise the exception" )
except UnrecognizedAirportError, e:
self.assertEquals( "func", e.args[0] )
self.assertEquals( "arg1", e.args[1] )
except Exception, e:
self.fail( "Raised the wrong exception" )

assertRaises is a bit simplistic, and doesn't let you test the details of the raised exception beyond it belonging to a specified class. For finer-grained testing of exceptions, you need to "roll your own" with a try/except/else block (you can do it once and for all in a def assertDetailedRaises method you add to your own generic subclass of unittest's test-case, then have your test cases all inherit your subclass instead of unittest's).

Related

class '_mysql_exceptions.DataError': How to check a variable type in an if condition

I need to return update/insert result, from the database class, back to the calling class to differentiate
between success and error.
An update/insert returns <type long'> while a database error returns
<class '_mysql_exceptions.DataError'>.
Since I am not sure about the return type during a success that it would always be a long type, I am checking for type class.
And, I couldn't do it. I tried these:
try:
x = cursor.execute(q, d)
conn.commit()
return x #Return this to the calling class
except MySQLdb.Error, e:
return e #Return this to the calling class
if isinstance(e, class): #Doesn't work
if issubclass(e, _mysql_exceptions): #Doesn't work
How do I check the type of e here?
If I am doing it all wrong, please suggest something nice, thanks.
The issue is that isinstance(obj, class) is not valid syntax, and _mysql_exceptions is a module, not an exception type, which raises a TypeError. To explicitly check an exception type, you can catch each individually:
from _mysql.exceptions import DataError, SomeOtherError, ...
from requests import HTTPError # as an example of a different error
try:
x = cursor.execute(q, d)
conn.commit()
except DataError as e:
# do something
except SomeOtherError as e:
# do something else
except HTTPError as e:
# your connection is broken
# maybe raise from e?
You need to catch that explicit error type, then you don't need to do if isinstance. Start with no exception handling at all, this will lead you to the exceptions that you do need to handle, and anything else should be considered unexpected and should cause the application to either crash or propagate some helpful error message to let you know something bad happened:
try:
some_function()
except ValueError as e:
# this is expected, and is handled accordingly
handle_expected_error()
# This is optional, normally a bare exception block is considered bad practice,
# but can allow your application to continue functioning while raising some
# helpful error so this isn't suppressed
except Exception as e:
# this is not expected, I'm going to propagate this error
# up to be obvious what happened
handle_unexpected_error()
#or
raise from e
Edit: What if I want a calling class to handle the exception?
Reasonable, and I would lean on catching the exception. Instead of handling the exception, I would allow the function to just raise the exception and handle it in the calling class. As a really simple example:
class MyClass:
def __init__(self, conn, cursor):
self.conn = conn
self.cursor = cursor
def some_function(self):
# This raises an error, note I'm not handling it here
x = self.cursor.execute()
self.conn.commit()
return x
def main_function(self):
try:
x = self.some_function()
except DataError as e:
handle_exception()
# unexpected, handle this here
except Exception as e:
raise from e
# or do something else

Assert exception message?

I'm using pytest in a project with a good many custom exceptions.
pytest provides a handy syntax for checking that an exception has been raised, however, I'm not aware of one that asserts that the correct exception message has been raised.
Say I had a CustomException that prints "boo!", how could I assert that "boo!" has indeed been printed and not, say, "<unprintable CustomException object>"?
#errors.py
class CustomException(Exception):
def __str__(self): return "ouch!"
#test.py
import pytest, myModule
def test_custom_error(): # SHOULD FAIL
with pytest.raises(myModule.CustomException):
raise myModule.CustomException == "boo!"
I think what you're looking for is:
def failer():
raise myModule.CustomException()
def test_failer():
with pytest.raises(myModule.CustomException) as excinfo:
failer()
assert str(excinfo.value) == "boo!"
You can use match keyword in raises. Try something like
with pytest.raises(
RuntimeError, match=<error string here>
):
pass

Is there a way to handle exceptions but only from a specific library?

I am using a package (foo) and am calling a method in a class (Foo) within that package. Let's say that package has its own exceptions defined:
exception foo.exceptions.FooEx_1
exception foo.exceptions.FooEx_2
...
exception foo.exceptions.FooEx_n
I'd prefer not to write a generic exception handler:
try:
except:
# Process any exception here
I just want to catch only exceptions raised in the foo library/package. Is there a way to do that? Like:
try:
except foo.exceptions.*
If all of the exceptions in foo.exceptions subclass some base foo.exceptions.BaseFooException class, you can catch it:
>>> assert issubclass(NotImplementedError, RuntimeError)
>>>
>>> try:
... raise NotImplementedError()
... except RuntimeError:
... print('Caught it')
...
Caught it
Otherwise, you will have to extract all of the exceptions from the module:
all_exceptions = tuple(getattr(foo.exceptions, e) for e in dir(foo.exceptions) if e.startswith('FooEx'))
And filter on them:
try:
...
except all_exceptions as e:
# We caught it

Catching an exceptions in __enter__ in the calling code in Python

Is there a way I can catch exceptions in the __enter__ method of a context manager without wrapping the whole with block inside a try?
class TstContx(object):
def __enter__(self):
raise Exception("I'd like to catch this exception")
def __exit__(self, e_typ, e_val, trcbak):
pass
with TstContx():
raise Exception("I don't want to catch this exception")
pass
I know that I can catch the exception within __enter__() itself, but can I access that error from the function that contains the with statement?
On the surface the question Catching exception in context manager __enter__() seems to be the same thing but that question is actually about making sure that __exit__ gets called, not with treating the __enter__ code differently from the block that the with statement encloses.
...evidently the motivation should be clearer. The with statement is setting up some logging for a fully automated process. If the program fails before the logging is set up, then I can't rely on the logging to notify me, so I have to do something special. And I'd rather achieve the effect without having to add more indentation, like this:
try:
with TstContx():
try:
print "Do something"
except Exception:
print "Here's where I would handle exception generated within the body of the with statement"
except Exception:
print "Here's where I'd handle an exception that occurs in __enter__ (and I suppose also __exit__)"
Another downside to using two try blocks is that the code that handles the exception in __enter__ comes after the code that handles exception in the subsequent body of the with block.
You can catch the exception using try/except inside of __enter__, then save the exception instance as an instance variable of the TstContx class, allowing you to access it inside of the with block:
class TstContx(object):
def __enter__(self):
self.exc = None
try:
raise Exception("I'd like to catch this exception")
except Exception as e:
self.exc = e
return self
def __exit__(self, e_typ, e_val, trcbak):
pass
with TstContx() as tst:
if tst.exc:
print("We caught an exception: '%s'" % tst.exc)
raise Exception("I don't want to catch this exception")
Output:
We caught an exception: 'I'd like to catch this exception'.
Traceback (most recent call last):
File "./torn.py", line 20, in <module>
raise Exception("I don't want to catch this exception")
Exception: I don't want to catch this exception
Not sure why you'd want to do this, though....
You can use contextlib.ExitStack as outlined in this doc example in order to check for __enter__ errors separately:
from contextlib import ExitStack
stack = ExitStack()
try:
stack.enter_context(TstContx())
except Exception: # `__enter__` produced an exception.
pass
else:
with stack:
... # Here goes the body of the `with`.

How to catch an exception in python and get a reference to the exception, WITHOUT knowing the type?

I'm wondering how I can catch any raised object (i.e. a type that does not extend Exception), and still get a reference to it.
I came across the desire to do this when using Jython. When calling a Java method, if that method raises an exception, it will not extend Python's Exception class, so a block like this will not catch it:
try:
# some call to a java lib that raises an exception here
except Exception, e:
# will never be entered
I can do this, but then I have no access to the exception object that was raised.
try:
# some call to a java lib that raises an exception here
except:
# will enter here, but there's no reference to the exception that was raised
I can solve this by importing the Java exception type and catching it explicitly, but this makes it difficult/impossible to write generic exception handling wrappers/decorators.
Is there a way to catch some arbitrary exception and still get a reference to it in the except block?
I should note that I'm hoping for the exception handling decorator I am making to be usable with Python projects, not just with Jython projects. I'd like to avoid importing java.lang.Exception because that just makes it Jython-only. For example, I figure I can do something like this (but I haven't tried it), but I'd like to avoid it if I can.
try:
# some function that may be running jython and may raise a java exception
except (Exception, java.lang.Exception), e:
# I imagine this would work, but it makes the code jython-only
You can reference exceptions using the sys module. sys.exc_info is a tuple of the type, the instance and the traceback.
import sys
try:
# some call to a java lib that raises an exception here
except:
instance = sys.exc_info()[1]
FWIW, I have found that if you add this import to your Jython script:
from java.lang import Exception
and just use the conventional Python Exception handler:
except Exception, e:
it will catch both Python exceptions and Java exceptions
Just for anyone interested... I spent a bit of time testing stuff because I wanted to find out how to get a proper stack trace whether a Python Exception (BaseException in fact, which is the base class) or a java.lang.Throwable (java base class for Exception, Error, etc.) is thrown... this code illustrates how to catch all line number refs correctly.
import sys
import traceback
import java
print "hello world"
def custom_hook( type, exc, tb ):
if isinstance( sys.exc_info()[ 1 ], java.lang.Throwable ):
sys.stderr.write( "AS JAVA:\n" )
sys.exc_info()[ 1 ].printStackTrace() # java part
else:
sys.stderr.write( "NO JAVA TRACE:\n" )
sys.stderr.write( "AS PYTHON:\n" )
traceback.print_exc()
# useful for custom exception handling!
sys.excepthook = custom_hook
def handle_exc():
# generate either a java.lang.Throwable (uncomment the next line and comment out "a = 16 / 0"
# java.lang.String( None )
# OR... a python-style BaseException:
a = 16 / 0
class Task( java.lang.Runnable ):
def run( self ):
# NB the problem with all this stuff is that the Java stack trace shows
# a java.lang.Throwable occurring at the last line of this code block...
# print "lots of stuff first"
# print "lots 2"
# handle_exc()
# print "lots 3"
# print "lots of stuff after"
try:
print "lots of stuff first"
print "lots 2"
handle_exc()
print "lots 3"
print "lots of stuff after"
# NB do not catch both (Python) BaseException and java.lang.Throwable...
# except ( BaseException, java.lang.Throwable ), e:
# the above means that the line no. in handle_exc is not shown when a BaseException
# is thrown...
except java.lang.Throwable, t:
tb = sys.exc_info()[ 2 ]
sys.stderr.write( "java.lang.Throwable thrown at: %s\n" % tb.tb_lineno )
raise t
java.awt.EventQueue.invokeAndWait( Task() )
After this one might think of writing a decorator to precede def run( self ) and similar methods so that you don't have to write out this catch-the-Throwable try-except block each time... specifically:
def throw_trap( function ):
def wrapper(*args, **kvargs):
try:
return function( *args, **kvargs )
except java.lang.Throwable, t:
tb = sys.exc_info()[ 2 ]
while( tb ):
sys.stderr.write( "thrown at: %s\n" % tb.tb_lineno )
tb = tb.tb_next
raise t
return wrapper
def handle_exc():
java.lang.String( None )
# a = 16 / 0
class Task( java.lang.Runnable ):
#throw_trap
def run( self ):
print "lots of stuff first"
print "lots 2"
handle_exc()
print "lots 3"
print "lots of stuff after"
java.awt.EventQueue.invokeAndWait( Task() )

Categories

Resources