views:

1243

answers:

3

What are the best practices for creating exceptions? I just saw this, and i don't know if i should be horrified, or like it. I read several times in books that exceptions should never ever hold a string, because strings themselves can throw exceptions. Any real truth to this?

Basically from my understanding from the scripts is that this was done so all the inhouse python libraries will have a common error message format (something that is desperately needed) so i can understand why putting the error message string is a good idea. (Almost every method throws exceptions due to the utter need for nothing invalid getting through).

The code in question is the following:

"""
Base Exception, Error
"""
class Error(Exception):
    def __init__(self, message):
        self.message = message

    def __str__(self):
        return "[ERROR] %s\n" % str(self.message)

    def log(self):
        ret = "%s" % str(self.message)
        if(hasattr(self, "reason")):
            return "".join([ret, "\n==> %s" % str(self.reason)])
        return ret

class PCSException(Error):
    def __init__(self, message, reason = None):
        self.message = message
        self.reason = reason
    def __str__(self):
        ret = "[PCS_ERROR] %s\n" % str(self.message)
        if(self.reason != None):
            ret += "[REASON] %s\n" % str(self.reason)
        return ret

This is just the tip of the iceberg, but can someone give me some insight in what makes this a terrible idea? Or if there is a much better exception coding process/style.

+6  A: 

"I read several times in books that exceptions should never ever hold a string, because strings themselves can throw exceptions. Any real truth to this?"

What?

Please provide a reference or a link to this. It's totally untrue.

Since all objects can throw exceptions, no object could be contained in an exception by that logic.

No, the "no strings" is simply crazy in a Python context. Perhaps you read it in a C++ context.


Edit

Once upon a time (back in the olden days) you could raise a Python exception by name instead of by the actual class.

raise "SomeNameOfAnExceptionClass"

This is bad. But this is not including a string inside an exception. This is naming the exception with a string instead of the actual class object. In 2.5, this can still work, but gets a deprecation warning.

Perhaps this is what you read "Do not raise an exception with a string name"

S.Lott
Your right now that i think about it. Most of them were C++ books, but i clearly remeber reading a python book, where it said in big bold letters "exceptions should never hold the error message, they should just hold a small identifier (number or maybe a small string)" i don't remeber the book, but i'll see if i can dig it up.
UberJumper
+1 -> I agree - nothing wrong with strings in exceptions as far as I'm aware.
Jon Cage
Maybe it was referring to the fact that you should not use an Exception class for multiple exceptional situations. Like MyFrameworkException('File not found') and MyFrameworkException('Database connection unavailable').
Ionuț G. Stan
+1 Having strings in exceptions is very useful. Other languages besides Python also support adding descriptive info to the error constructs or exceptions... they may call it a "context" or a "reason", and may represent it as a string or some other object, but it's still the same deal.
Jarret Hardie
I always put some message useful for the developer into the exception - they, and the log output, are for development purposes as far as I'm concerned. Possibly the book you read was by someone who likes to handle user-facing errors using exceptions.
Sii
Err, I don't think you're correct about how raise used to work. raise used to accept strings because that string *would be the exception*. It wouldn't take the string and instantiate some other object, it would literally throw the string. Or so I thought. Thus why there's python code like: raise "Here is my nice verbose error message"
Joseph Garvin
+1  A: 

First impression is that it's entirely too much code for an exception.

Formatting exceptions should be done in logger configuration. Same goes for the logging itself.

It also redefines the standard (and deprecated) message attribute, and doesn't call the superclass constructor. (This might or might not break Python 3.0 exception chaining, I haven't tried because I'm running 2.6)

Most of what the extra code does can be realised using BaseException.args, by logging the following as the "message":

'\n==> '.join(exception.args)

I'd argue that if something can be done using a common / idiomatic mechanism, it should especially be done so in exception handling. (Exceptions being a mechanism to signal something across application layers.)

Personally, I try to avoid anything beyond

class SomeException(Exception): pass

(Disclaimer: answer subjective, possibly by nature of the question.)

Sii
The main problem with a logger is that some of the scripts are like 20-30 lines. All output is piped to the console by default (and has to be done)
UberJumper
You could likely externalise logging configuration into a shared module just like you seem to externalise formatting and logging errors into their definitions. Separation of concerns usually tends to clash with absolute code size though.
Sii
+15  A: 

Robust exception handling (in Python) - a "best practices for Python exceptions" blog post I wrote a while ago. You may find it useful.

Eli Bendersky
Thanks that was very informative.
UberJumper