Python Exception Hierarchy

05 Mar 2016

Exception handling is a common task we all encounter as a software engineer. But, to properly handle exceptions, the first thing to understand is the hierarchy of standard exception objects for an OOP language; the hierarchy is important as it determines which exception handlers (except statement in Python) should handle which exceptions, and such knowledge is crucial in implementing effective error handling, as you might want to keep the error handling as narrow as possible in most circumstances. Here we will focus on Python’s Exception objects’ hierarchy.

Since Python 2.5, the base exception handling class is BaseException, every other exception class subclasses from it (in other words, it is the root of everything). Actually, that is all you need to know, because you can roll out your own function to print the entire hierarchy:

def classtree(cls, indent=0):
    print '.' * indent, cls.__name__
    for subcls in cls.__subclasses__():
        classtree(subcls, indent + 3)

classtree(BaseException)

Voila, and here is the result of the hierarchy for Python 2.7:

BaseException
... Exception
...... StandardError
......... TypeError
......... ImportError
............ ZipImportError
......... EnvironmentError
............ IOError
............... ItimerError
............ OSError
......... EOFError
......... RuntimeError
............ NotImplementedError
......... NameError
............ UnboundLocalError
......... AttributeError
......... SyntaxError
............ IndentationError
............... TabError
......... LookupError
............ IndexError
............ KeyError
............ CodecRegistryError
......... ValueError
............ UnicodeError
............... UnicodeEncodeError
............... UnicodeDecodeError
............... UnicodeTranslateError
......... AssertionError
......... ArithmeticError
............ FloatingPointError
............ OverflowError
............ ZeroDivisionError
......... SystemError
............ CodecRegistryError
......... ReferenceError
......... MemoryError
......... BufferError
...... StopIteration
...... Warning
......... UserWarning
......... DeprecationWarning
......... PendingDeprecationWarning
......... SyntaxWarning
......... RuntimeWarning
......... FutureWarning
......... ImportWarning
......... UnicodeWarning
......... BytesWarning
...... _OptionError
...... error
... GeneratorExit
... SystemExit
... KeyboardInterrupt

Additionally, inspect module has a relevant function getclasstree() which walks the inheritance structure from bottom (the specific class you are intereseted in) to top (BaseException) and does not print the entire tree, only the path of the inheritance structure for that specific class, for example:

In [17]: inspect.getclasstree(inspect.getmro(BaseException))
Out[17]: [(object, ()), [(BaseException, (object,))]]

In [16]: inspect.getclasstree(inspect.getmro(UnicodeError))
Out[16]: 
[(object, ()),
 [(BaseException, (object,)),
  [(Exception, (BaseException,)),
   [(StandardError, (Exception,)),
    [(ValueError, (StandardError,)), [(UnicodeError, (ValueError,))]]]]]]

Check out the built-in Exceptions documentation for Python 2 and Python 3 as they also contain the information about the complete Exception hierarchy diagrams.