0

Assume two Python classes, the first of which (Class 1) contains a function (function_c1) that encompasses some statement (some_statement), which - if true - returns a customized exception (MyException). Further assume that the second class (Class 2) executes the first class and wishes to pass on the customized exception (i.e., return the same exception itself). The following code exemplifies this situation:

class Class1:
    ''' My nifty class 1. '''
    def __init__(self):
        pass
    def function_c1(self, some_input):
        ''' A function of class 1. '''
        if some_statement(some_input):
            return MyException('Some error message.')
        return True

class Class2:
    ''' My nifty class 2. '''
    def __init__(self):
        pass
    def function_c2(self, some_input):
        ''' A function of class 2. '''
        my_handle = Class1()
        try:
            my_handle.function_c1(some_input)
        except MyException as e:
            return MyException(e)
        return True

Why does Class 2 return 'True' irrespective of whether Class 1 returns the exception?


EDIT 1: In the example above, I had deliberately chosen to return instead of to raise the exception in order to render class 1 applicable to unit-testing.

self.assertRaises(Class1().function_c1(some_input))

Given my admittedly incomplete understanding of Python, I see only two options to eat the cake (i.e., conduct unit-testing) and keep it too (i.e., have class 2 receive the exception from class 1): (a) I either raise as well as return MyException in Class 1 (if that were even possible?), or (b) I use a different assert statement.


EDIT 2: Thanks to some excellent feedback, I now understand that my original mistake was the result of having incorrectly used assertRaises. It should have been used as below:

class MyException(Exception):
    pass
class Class1:
    ...

with self.assertRaises(MyException):
    Class1().function_c1(some_input)

However, the unit-test only passes when Class 1 raises a built-in exception, such as ValueError (and consequently using with self.assertRaises(ValueError):, of course). My user-defined exception MyException still does not pass the unit-test. Why could that be?

Michael Gruenstaeudl
  • 1,609
  • 1
  • 17
  • 31
  • 1
    There's a difference between _returning_ an exception an `raising` one. – mgilson Feb 18 '16 at 14:34
  • Possible duplicate of [Manually raising (throwing) an exception in Python](http://stackoverflow.com/questions/2052390/manually-raising-throwing-an-exception-in-python) – Keith Hall Feb 18 '16 at 14:34
  • Where do you ever use `Class2`? If you ever call `Class2.function_c2`, it should raise an AttributeError, since an instance of `Class2` doesn't have a `function_c1` method. – chepner Feb 18 '16 at 15:06
  • @chepner You are quite right. The above example had a mistake, which is now corrected. – Michael Gruenstaeudl Feb 18 '16 at 16:28

1 Answers1

1

In order for an exception to be catchable, you have to raise it. If you return it, the exception acts like any other python object (and doesn't get caught in a try block.

class Class1:
    ''' My nifty class 1. '''
    def __init__(self):
        pass
    def function_c1(self, some_input):
        ''' A function of class 1. '''
        if some_statement(some_input):
            raise MyException('Some error message.')
        return True

To address your comment below, for unit-testing with python's unittest module, you'd probably do something like this:

with self.assertRaises(MyException):
    Class1().function_c1(...)

If you're stuck in a pre-python2.7 environment, it would be:

self.assertRaises(MyException, Class1().function_c1, ...)

But for your sake, I hope you can use python2.7 at least :-).

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • You *just* beat me to it. – zondo Feb 18 '16 at 14:36
  • @mgilson Right, but I also wish to apply some _unittest_ to Class1 (i.e., `self.assertRaises(Class1().function_c1(some_input))`), which appears to require the exception returned, not raised by class 1. Is there a way to **both** _raise_ and _return_? – Michael Gruenstaeudl Feb 18 '16 at 14:45
  • 1
    @MichaelGruenstaeudl I think you need to read the documentation for [**`assertRaises`**](https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises). That's not how you call it. – Peter Wood Feb 18 '16 at 14:53
  • @MichaelGruenstaeudl -- I've updated with an example on how you would use `assertRaises` to test this. – mgilson Feb 18 '16 at 15:38
  • @PeterWood You are quite right that I had incorrectly called `assertRaises`. The original example has now been corrected. However, the unit-test only passes when _Class 1_ raises a built-in exception (e.g., `ValueError`) and, consequently, when `with assertRaises(ValueError):`. My user-defined exception _MyException_ does not pass the unit-test. – Michael Gruenstaeudl Feb 18 '16 at 17:33
  • @mgilson Thank you for your update. However, my user-defined exception _MyException_ still does not pass the unit-test, even under your corrected `assertRaises` example. Raising a _built-in_ (e.g., `ValueError`) instead of my user-defined exception does, by contrast, pass the test. See _Edit 2_ in the example above. Why could that be? – Michael Gruenstaeudl Feb 18 '16 at 17:46