subclassing Exceptions

Tom Bryan tbryan at python.net
Sat Jul 21 22:46:25 EDT 2001


Sheila King wrote:

> On Sun, 22 Jul 2001 00:51:31 -0500, "Volucris" <volucris at hotmail.com>
> wrote in comp.lang.python in article
> <3b5a69a3$0$320$6e49188b at news.goldengate.net>:
> 
> :If all you want is your own exception (nothing fancy), all you need is a
> :class:
> :
> :class SpamException:
> :    pass
> 
> OK, interesting. I didn't realize you could make an exception without
> subclassing it. So, I tried this script:

Neither did I, but you probably don't want to do that.

One reason to subclass from a common Exception base class is so 
that exceptions can be caught based on inheritence.  Also, since 
there's a common exception base class, all exceptions have common 
behavior that you'd have to recode if you didn't subclass.  

> Hmm...I just tried a little experiment in the interactive shell to see
> if I could confirm/deny my above theory, and it gave me surprising
> results that I do not understand, and which seem to indicate no
> difference between deriving it from an Exception class or not.

Consider:

----- test1.py -----
class SpamException:
    pass

try:
    while (1):
        inputstring = raw_input("Enter any string, except 'spam': ")
        if inputstring == "spam":
            raise SpamException
# Catch *all* exceptions...
# at least those that subclass Exception like they should. :-)
except Exception, data:
    print "Ahhh!  ", data
-----------------

If I run this, the except block is unable to catch SpamException, 
although it looks like I should be catching all exceptions since I'm 
catching the base class of all exceptions:

Enter any string, except 'spam': spam
Traceback (most recent call last):
  File "test_exception.py", line 11, in ?
    raise SpamException
__main__.SpamException: <__main__.SpamException instance at 0x80d6cb4>

----- test2.py -----    
class SpamException(Exception):
    pass

try:
    while (1):
        inputstring = raw_input("Enter any string, except 'spam': ")
        if inputstring == "spam":
            raise SpamException( 'User typed "spam."' )
# Catch *all* exceptions...
# at least those that subclass Exception like they should. :-)
except Exception, data:
    print "Ahhh!  ", data
----------------    

Since I sublcass Exception, the except block will be able to catch my 
exception, and the SpamException automatically gets a constructor 
that takes an error message as an argument.  See:

Enter any string, except 'spam': spam
Ahhh!   User typed "spam."

> Well, I am interested in further enlightenment on this topic...?

I believe that the intent is for all exception classes to subclass from 
Exception or one of its sublcasses.  I think that a common idiom is for 
a module to declare its own base exception class.  Then, it can declare 
many subclasses of *that* Exception subclass to use internally.  People 
using the module can simply catch the base FooException unless they 
have a good reason to get more specific.  Basically, they called code 
in your module, and it blew up.  Often, that's as much as they can say, 
and the error message attached to the exception *should be* enlightening 
enough to troubleshoot the problem.  

For example, 

class StackException( Exception ):
    pass
class EmptyStackException( StackException ):
    pass
class StackOverflowException( StackException ):
    pass

class Stack:
    """An implementation of the Stack data structure
    that supports a maximum stack stize. """
    def __init__( self, maxSize=None ):
        try: 
            self.stack = []
            self.max = int( maxSize )
        except ValueError:  
            #value error is thrown when maxSize isn't an integer
            raise StackException( "Illegal value for Stack's maximum size: %s." % maxSize )
    def push( self, data ):
        if self.max != None and len(self.stack) == self.max:
            raise StackOverflowException( "Stack already at maximum size %d" % self.max )
        self.stack.append( data )
    def pop( self ):
        if len( self.stack ) == 0:
            raise EmptyStackException( "Cannot pop from an empty stack" )
        self.stack, data = self.stack[:-1], self.stack[-1]
        return data
    def peek( self ):
        if len( self.stack ) == 0:
            raise EmptyStackException( "Cannot peek at an empty stack" )
        return self.stack[ -1 ]

if __name__ == '__main__':
    # Note that our code can simply catch the StackException base class
    try:
        aStack = Stack( 'hi' )
    except StackException, data:
        print "Exception for Stack('hi'):", data

    try:
        aStack = Stack( 2 )
        print aStack.peek()
    except StackException, data:
        print "Exception for aStack.peek():", data

    try:
        print aStack.pop()
    except StackException, data:
        print "Exception for aStack.pop():", data

    try:
        aStack.push( 'one' )
        aStack.push( 'two' )
        aStack.push( 'three' )
    except StackException, data:
        print "Exception for aStack.push('three'):", data


Of course, this advice might be slightly off.  Python's exception stuff looks 
a lot like the Java exception stuff, and I've done a lot more Java.  

Exception-"too-many-languages"-ly yours
---Tom




More information about the Python-list mailing list