4

I am customizing exceptions in my python code. I have inherited exception class in to other and now defining some custom errors as classes derived from my custom exception class like this:

class DataCollectorError(Exception): pass
class ParamNullError(DataCollectorError) : pass
class ParamInvalidTypeError(DataCollectorError) : pass

I am raising these exceptions in my python function like:

def READ_METER_DATA (regIndex, numRegisters, slaveUnit):
    try:
        if not regIndex:
            raise ParamNullError, "register index is null"

        if not numRegisters:
            raise ParamNullError, "number of registers should not be null"

        if not slaveUnit:
            raise ParamNullError, "Meter Id should not be null"

and logging error like :

except DataCollectorError as d:
    lgr.error('DataCollector Error(READ_METER_DATA): '+d.args[0])
    print 'DataCollector Error:(READ_METER_DATA)', d.args[0]
except:
    lgr.error('Unexpected Error: ', sys.exc_info())
    print 'Unexpected Error: ', sys.exc_info()
    pass

but this defeats purpose of unit testing script as it doesn't whether exception raised bcz it is being catched by my catch block before my unit test script knows it. so i wanted to log these errors in base class itself -

Class ParamNullError(DataCollectorError):
    <----here----------->
    pass 

can anybody tell me how to fetch that string passed while raising exception?

Inderpal Singh
  • 270
  • 2
  • 8
  • 24
  • You don't need to customize logging for your exceptions the way you did; use `lgr.exception()` instead and have the `logging` module handle exceptions *automatically* (no need to call `sys.exc_info()` either. Custom exceptions like yours will be handled without any special casing. – Martijn Pieters May 28 '13 at 09:58

2 Answers2

7

Simply extend your error-class with an __init__ and an __str__ method.

Example:

class DataCollectorError(Exception):
    def __init__(self, msg=''):
        self.msg = msg
        log(msg)  # use your logging things here

    def __str__(self):
        return self.msg

Use msg='' because then you don't need to always specify a message.

TobiMarg
  • 3,667
  • 1
  • 20
  • 25
  • if i will move my exception raising part out of try is there any possibility that program fails while checking any if and i will not be able to catch it as its out of try.. can program fail there? because i don't want it to fail without anything in logs – Inderpal Singh May 28 '13 at 10:18
  • I don't know if I understand correct, but with my code (you have to replace `log` with your logging function) the exception will be raised, but before the program stops (if you don't catch the exception) it will write the message into the log (everything you do in the `__init__` will be executed, before an exit). If you want to ignore the exception silently you have to have the raise in your `try ... except` block – TobiMarg May 28 '13 at 10:32
1

Don't.

Factor out the calls you need to unit test, and move your exception handler out:

try:
     testableFunctionCall()
except:
     lgr.exception('Unexpected Error')

and test testableFunctionCall().

Alternatively, use the testfixtures library to test the logging itself:

from testfixtures import LogCapture
with LogCapture() as l:
    callFunctionUnderTest()

l.check(
     ('packagename', 'ERROR', 'DataCollector Error(READ_METER_DATA): foobar'),
)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • if i will move my exception raising part out of try is there any possibility that program fails while checking any if and i will not be able to catch it as its out of try.. can program fail there? because i don't want it to fail without anything in logs – Inderpal Singh May 28 '13 at 10:13
  • @InderpalSingh: Moving the functionality to a function won't change what happens when an exception is raised. Unless you call that function in *new* places, the exception will still be caught by that handler. – Martijn Pieters May 28 '13 at 10:18