PyMOTW: cgitb - Detailed traceback reports

By Doug Hellmann
January 30, 2010

cgitb – Detailed traceback reports

Purpose:cgitb provides more detailed traceback information than traceback.
Python Version:2.2 and later

cgitb was originally designed for showing errors and debugging information in web applications. It was later updated to include plain-text output as well, but unfortunately wasn’t renamed. This has lead to obscurity and the module is not used as often as it should be. Nonetheless, cgitb is a valuable debugging tool in the standard library.

Standard Traceback Dumps

Python’s default exception handling behavior is to print a tracebackto standard error with the call stack leading up to the error position. This basic output is frequently enough information to understand the cause of the exception and permit a fix.

def func1(arg1):
    local_var = arg1 * 2
    return func2(local_var)

def func2(arg2):
    local_var = arg2 + 2
    return func3(local_var)

def func3(arg3):
    local_var = arg2 / 2
    return local_var

func1(1)

The above sample program has a subtle error in func3().

$ python cgitb_basic_traceback.py
Traceback (most recent call last):
  File "cgitb_basic_traceback.py", line 22, in <module>
    func1(1)
  File "cgitb_basic_traceback.py", line 12, in func1
    return func2(local_var)
  File "cgitb_basic_traceback.py", line 16, in func2
    return func3(local_var)
  File "cgitb_basic_traceback.py", line 19, in func3
    local_var = arg2 / 2
NameError: global name 'arg2' is not defined

Enabling Detailed Tracebacks

While the basic traceback includes enough information for us to spot the error, enabling cgitb replaces sys.excepthook with a function that gives extended tracebacks with even more detail.

import cgitb
cgitb.enable(format='text')

def func1(arg1):
    local_var = arg1 * 2
    return func2(local_var)

def func2(arg2):
    local_var = arg2 + 2
    return func3(local_var)

def func3(arg3):
    local_var = arg2 / 2
    return local_var

func1(1)

As you can see below, the error report is much more extensive. Each frame of the stack is listed, along with:

  • the full path to the source file, instead of just the base name
  • the values of the arguments to each function in the stack
  • a few lines of source context from around the line in the error path
  • the values of variables in the expression causing the error
$ python cgitb_extended_traceback.py
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<type 'exceptions.NameError'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:27 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_extended_traceback.py in <module>()
   21 def func3(arg3):
   22     local_var = arg2 / 2
   23     return local_var
   24
   25 func1(1)
func1 = <function func1 at 0x7bbb0>

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_extended_traceback.py in func1(arg1=1)
   13 def func1(arg1):
   14     local_var = arg1 * 2
   15     return func2(local_var)
   16
   17 def func2(arg2):
global func2 = <function func2 at 0x7bbf0>
local_var = 2

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_extended_traceback.py in func2(arg2=2)
   17 def func2(arg2):
   18     local_var = arg2 + 2
   19     return func3(local_var)
   20
   21 def func3(arg3):
global func3 = <function func3 at 0x7bc30>
local_var = 4

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_extended_traceback.py in func3(arg3=4)
   20
   21 def func3(arg3):
   22     local_var = arg2 / 2
   23     return local_var
   24
local_var undefined
arg2 undefined
<type 'exceptions.NameError'>: global name 'arg2' is not defined
    __class__ = <type 'exceptions.NameError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.NameError object at 0x81710>
    __dict__ = {}
    __doc__ = 'Name not found globally.'
    __format__ = <built-in method __format__ of exceptions.NameError object at 0x81710>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.NameError object at 0x81710>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.NameError object at 0x81710>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.NameError object at 0x81710>
    __hash__ = <method-wrapper '__hash__' of exceptions.NameError object at 0x81710>
    __init__ = <method-wrapper '__init__' of exceptions.NameError object at 0x81710>
    __new__ = <built-in method __new__ of type object at 0x5b0aa0>
    __reduce__ = <built-in method __reduce__ of exceptions.NameError object at 0x81710>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.NameError object at 0x81710>
    __repr__ = <method-wrapper '__repr__' of exceptions.NameError object at 0x81710>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.NameError object at 0x81710>
    __setstate__ = <built-in method __setstate__ of exceptions.NameError object at 0x81710>
    __sizeof__ = <built-in method __sizeof__ of exceptions.NameError object at 0x81710>
    __str__ = <method-wrapper '__str__' of exceptions.NameError object at 0x81710>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0x5b0aa0>
    __unicode__ = <built-in method __unicode__ of exceptions.NameError object at 0x81710>
    args = ("global name 'arg2' is not defined",)
    message = "global name 'arg2' is not defined"

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_extended_traceback.py", line 25, in <module>
    func1(1)
  File "cgitb_extended_traceback.py", line 15, in func1
    return func2(local_var)
  File "cgitb_extended_traceback.py", line 19, in func2
    return func3(local_var)
  File "cgitb_extended_traceback.py", line 22, in func3
    local_var = arg2 / 2
NameError: global name 'arg2' is not defined

The end of the output also includes the full details of the exception object (in case it has attributes other than message that would be useful for debugging) and the original form of a traceback dump.

Local Variables in Tracebacks

Having access to the variables involved in the error stack can help find a logical error that occurs somewhere higher in the stack than the line where the actual exception is generated.

import cgitb
cgitb.enable(format='text')

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    return func2(a, c)

func1(1, 5)

In the case of this code with a ZeroDivisionError, we can see that the problem is introduced in the computation of the value of c in func1(), rather than where the value is used in func2().

$ python cgitb_local_vars.py
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<type 'exceptions.ZeroDivisionError'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:27 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_local_vars.py in <module>()
   16 def func1(a, b):
   17     c = b - 5
   18     return func2(a, c)
   19
   20 func1(1, 5)
func1 = <function func1 at 0x7bbf0>

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_local_vars.py in func1(a=1, b=5)
   16 def func1(a, b):
   17     c = b - 5
   18     return func2(a, c)
   19
   20 func1(1, 5)
global func2 = <function func2 at 0x7bbb0>
a = 1
c = 0

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_local_vars.py in func2(a=1, divisor=0)
   12
   13 def func2(a, divisor):
   14     return a / divisor
   15
   16 def func1(a, b):
a = 1
divisor = 0
<type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
    __class__ = <type 'exceptions.ZeroDivisionError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object at 0x825a8>
    __dict__ = {}
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object at 0x825a8>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object at 0x825a8>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object at 0x825a8>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object at 0x825a8>
    __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object at 0x825a8>
    __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object at 0x825a8>
    __new__ = <built-in method __new__ of type object at 0x5af9c0>
    __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object at 0x825a8>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object at 0x825a8>
    __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object at 0x825a8>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object at 0x825a8>
    __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object at 0x825a8>
    __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object at 0x825a8>
    __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object at 0x825a8>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0x5af9c0>
    __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object at 0x825a8>
    args = ('integer division or modulo by zero',)
    message = 'integer division or modulo by zero'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_local_vars.py", line 20, in <module>
    func1(1, 5)
  File "cgitb_local_vars.py", line 18, in func1
    return func2(a, c)
  File "cgitb_local_vars.py", line 14, in func2
    return a / divisor
ZeroDivisionError: integer division or modulo by zero

The code in cgitb that examines the variables used in the stack frame leading to the error is smart enough to evaluate object attributes to display them, too.

import cgitb
cgitb.enable(format='text')

class BrokenClass(object):
    """This class has an error.
    """
    
    def __init__(self, a, b):
        """Be careful passing arguments in here.
        """
        self.a = a
        self.b = b
        self.c = self.a * self.b
        self.d = self.a / self.b
        return

o = BrokenClass(1, 0)

Here we see that self.a and self.b are involved in the error-prone code.

$ python cgitb_with_classes.py
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<type 'exceptions.ZeroDivisionError'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:27 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_with_classes.py in <module>()
   24         return
   25
   26 o = BrokenClass(1, 0)
   27
   28
o undefined
BrokenClass = <class '__main__.BrokenClass'>

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_with_classes.py in __init__(self=<__main__.BrokenClass object at 0x81e30>, a=1, b=0)
   21         self.b = b
   22         self.c = self.a * self.b
   23         self.d = self.a / self.b
   24         return
   25
self = <__main__.BrokenClass object at 0x81e30>
self.d undefined
self.a = 1
self.b = 0
<type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
    __class__ = <type 'exceptions.ZeroDivisionError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object at 0x7f710>
    __dict__ = {}
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object at 0x7f710>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object at 0x7f710>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object at 0x7f710>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object at 0x7f710>
    __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object at 0x7f710>
    __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object at 0x7f710>
    __new__ = <built-in method __new__ of type object at 0x5af9c0>
    __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object at 0x7f710>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object at 0x7f710>
    __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object at 0x7f710>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object at 0x7f710>
    __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object at 0x7f710>
    __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object at 0x7f710>
    __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object at 0x7f710>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0x5af9c0>
    __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object at 0x7f710>
    args = ('integer division or modulo by zero',)
    message = 'integer division or modulo by zero'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_with_classes.py", line 26, in <module>
    o = BrokenClass(1, 0)
  File "cgitb_with_classes.py", line 23, in __init__
    self.d = self.a / self.b
ZeroDivisionError: integer division or modulo by zero

Adding More Context

Suppose your function includes a lot of inline comments, whitespace, or other code that makes it very long. Having the default of 5 lines of context may not be enough help in that case, if the body of the function is pushed out of the code window displayed. Using a larger context value when enabling cgitb gets around this.

import cgitb
import sys

context_length = int(sys.argv[1])
cgitb.enable(format='text', context=context_length)

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    # Really
    # long
    # comment
    # goes
    # here.
    return func2(a, c)

func1(1, 5)

You can pass context to cgitb.enable() to control the amount of code displayed for each line of the traceback.

$ python cgitb_more_context.py 5
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<type 'exceptions.ZeroDivisionError'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:27 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_more_context.py in <module>()
   24     # goes
   25     # here.
   26     return func2(a, c)
   27
   28 func1(1, 5)
func1 = <function func1 at 0x7bbb0>

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_more_context.py in func1(a=1, b=5)
   24     # goes
   25     # here.
   26     return func2(a, c)
   27
   28 func1(1, 5)
global func2 = <function func2 at 0x7bb70>
a = 1
c = 0

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_more_context.py in func2(a=1, divisor=0)
   15
   16 def func2(a, divisor):
   17     return a / divisor
   18
   19 def func1(a, b):
a = 1
divisor = 0
<type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
    __class__ = <type 'exceptions.ZeroDivisionError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object at 0x816c0>
    __dict__ = {}
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object at 0x816c0>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object at 0x816c0>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object at 0x816c0>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object at 0x816c0>
    __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object at 0x816c0>
    __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object at 0x816c0>
    __new__ = <built-in method __new__ of type object at 0x5af9c0>
    __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object at 0x816c0>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object at 0x816c0>
    __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object at 0x816c0>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object at 0x816c0>
    __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object at 0x816c0>
    __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object at 0x816c0>
    __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object at 0x816c0>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0x5af9c0>
    __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object at 0x816c0>
    args = ('integer division or modulo by zero',)
    message = 'integer division or modulo by zero'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_more_context.py", line 28, in <module>
    func1(1, 5)
  File "cgitb_more_context.py", line 26, in func1
    return func2(a, c)
  File "cgitb_more_context.py", line 17, in func2
    return a / divisor
ZeroDivisionError: integer division or modulo by zero

Increasing the value gets us enough of the function that we can spot the problem in the code, again.

$ python cgitb_more_context.py 10
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<type 'exceptions.ZeroDivisionError'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:28 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_more_context.py in <module>()
   19 def func1(a, b):
   20     c = b - 5
   21     # Really
   22     # long
   23     # comment
   24     # goes
   25     # here.
   26     return func2(a, c)
   27
   28 func1(1, 5)
func1 = <function func1 at 0x7bbb0>

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_more_context.py in func1(a=1, b=5)
   19 def func1(a, b):
   20     c = b - 5
   21     # Really
   22     # long
   23     # comment
   24     # goes
   25     # here.
   26     return func2(a, c)
   27
   28 func1(1, 5)
global func2 = <function func2 at 0x7bb70>
a = 1
c = 0

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_more_context.py in func2(a=1, divisor=0)
   12
   13 context_length = int(sys.argv[1])
   14 cgitb.enable(format='text', context=context_length)
   15
   16 def func2(a, divisor):
   17     return a / divisor
   18
   19 def func1(a, b):
   20     c = b - 5
   21     # Really
a = 1
divisor = 0
<type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
    __class__ = <type 'exceptions.ZeroDivisionError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object at 0x816c0>
    __dict__ = {}
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object at 0x816c0>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object at 0x816c0>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object at 0x816c0>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object at 0x816c0>
    __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object at 0x816c0>
    __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object at 0x816c0>
    __new__ = <built-in method __new__ of type object at 0x5af9c0>
    __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object at 0x816c0>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object at 0x816c0>
    __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object at 0x816c0>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object at 0x816c0>
    __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object at 0x816c0>
    __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object at 0x816c0>
    __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object at 0x816c0>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0x5af9c0>
    __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object at 0x816c0>
    args = ('integer division or modulo by zero',)
    message = 'integer division or modulo by zero'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_more_context.py", line 28, in <module>
    func1(1, 5)
  File "cgitb_more_context.py", line 26, in func1
    return func2(a, c)
  File "cgitb_more_context.py", line 17, in func2
    return a / divisor
ZeroDivisionError: integer division or modulo by zero

Exception Properties

In addition to the local variables from each stack frame, cgitb shows all properties of the exception object. If you have a custom exception type with extra properties, they are printed as part of the error report.

import cgitb
cgitb.enable(format='text')

class MyException(Exception):
    """Add extra properties to a special exception
    """
    
    def __init__(self, message, bad_value):
        self.bad_value = bad_value
        Exception.__init__(self, message)
        return

raise MyException('Normal message', bad_value=99)

In this example, the bad_value property is included along with the standard message and args values.

$ python cgitb_exception_properties.py
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<class '__main__.MyException'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:28 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_exception_properties.py in <module>()
   18         self.bad_value = bad_value
   19         Exception.__init__(self, message)
   20         return
   21
   22 raise MyException('Normal message', bad_value=99)
MyException = <class '__main__.MyException'>
bad_value undefined
<class '__main__.MyException'>: Normal message
    __class__ = <class '__main__.MyException'>
    __delattr__ = <method-wrapper '__delattr__' of MyException object at 0x7f698>
    __dict__ = {'bad_value': 99}
    __doc__ = 'Add extra properties to a special exception\n    '
    __format__ = <built-in method __format__ of MyException object at 0x7f698>
    __getattribute__ = <method-wrapper '__getattribute__' of MyException object at 0x7f698>
    __getitem__ = <method-wrapper '__getitem__' of MyException object at 0x7f698>
    __getslice__ = <method-wrapper '__getslice__' of MyException object at 0x7f698>
    __hash__ = <method-wrapper '__hash__' of MyException object at 0x7f698>
    __init__ = <bound method MyException.__init__ of MyException('Normal message',)>
    __module__ = '__main__'
    __new__ = <built-in method __new__ of type object at 0x5aebc0>
    __reduce__ = <built-in method __reduce__ of MyException object at 0x7f698>
    __reduce_ex__ = <built-in method __reduce_ex__ of MyException object at 0x7f698>
    __repr__ = <method-wrapper '__repr__' of MyException object at 0x7f698>
    __setattr__ = <method-wrapper '__setattr__' of MyException object at 0x7f698>
    __setstate__ = <built-in method __setstate__ of MyException object at 0x7f698>
    __sizeof__ = <built-in method __sizeof__ of MyException object at 0x7f698>
    __str__ = <method-wrapper '__str__' of MyException object at 0x7f698>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0xa16890>
    __unicode__ = <built-in method __unicode__ of MyException object at 0x7f698>
    __weakref__ = None
    args = ('Normal message',)
    bad_value = 99
    message = 'Normal message'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_exception_properties.py", line 22, in <module>
    raise MyException('Normal message', bad_value=99)
MyException: Normal message

Logging Tracebacks

For many situations, printing the traceback details to standard error is the best resolution. In a production system, however, logging the errors is even better. cgitb.enable() includes an optional argument, logdir, to enable error logging. When a directory name is provided, each exception is logged to its own file in the given directory.

import cgitb
import os

cgitb.enable(logdir=os.path.join(os.path.dirname(__file__), 'LOGS'),
             display=False,
             format='text',
             )

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    return func2(a, c)

func1(1, 5)

Event with the error display suppressed, a message is printed describing where to go to find the error log.

$ python cgitb_log_exception.py
/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/cgitb.py:245: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
  value = pydoc.text.repr(getattr(evalue, name))
<p>A problem occurred in a Python script.
<p> /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/LOGS/tmpohlTP8.txt contains the description of this error.

$ ls LOGS
tmpohlTP8.txt

$ cat LOGS/*.txt
<type 'exceptions.ZeroDivisionError'>
Python 2.6.2: /Users/dhellmann/.virtualenvs/pymotw/bin/python
Sat Jan 30 12:50:28 2010

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_log_exception.py in <module>()
   21 def func1(a, b):
   22     c = b - 5
   23     return func2(a, c)
   24
   25 func1(1, 5)
func1 = <function func1 at 0x7bbb0>

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_log_exception.py in func1(a=1, b=5)
   21 def func1(a, b):
   22     c = b - 5
   23     return func2(a, c)
   24
   25 func1(1, 5)
global func2 = <function func2 at 0x7bb70>
a = 1
c = 0

 /Users/dhellmann/Documents/PyMOTW/src/PyMOTW/cgitb/cgitb_log_exception.py in func2(a=1, divisor=0)
   17
   18 def func2(a, divisor):
   19     return a / divisor
   20
   21 def func1(a, b):
a = 1
divisor = 0
<type 'exceptions.ZeroDivisionError'>: integer division or modulo by zero
    __class__ = <type 'exceptions.ZeroDivisionError'>
    __delattr__ = <method-wrapper '__delattr__' of exceptions.ZeroDivisionError object at 0x81710>
    __dict__ = {}
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __format__ = <built-in method __format__ of exceptions.ZeroDivisionError object at 0x81710>
    __getattribute__ = <method-wrapper '__getattribute__' of exceptions.ZeroDivisionError object at 0x81710>
    __getitem__ = <method-wrapper '__getitem__' of exceptions.ZeroDivisionError object at 0x81710>
    __getslice__ = <method-wrapper '__getslice__' of exceptions.ZeroDivisionError object at 0x81710>
    __hash__ = <method-wrapper '__hash__' of exceptions.ZeroDivisionError object at 0x81710>
    __init__ = <method-wrapper '__init__' of exceptions.ZeroDivisionError object at 0x81710>
    __new__ = <built-in method __new__ of type object at 0x5af9c0>
    __reduce__ = <built-in method __reduce__ of exceptions.ZeroDivisionError object at 0x81710>
    __reduce_ex__ = <built-in method __reduce_ex__ of exceptions.ZeroDivisionError object at 0x81710>
    __repr__ = <method-wrapper '__repr__' of exceptions.ZeroDivisionError object at 0x81710>
    __setattr__ = <method-wrapper '__setattr__' of exceptions.ZeroDivisionError object at 0x81710>
    __setstate__ = <built-in method __setstate__ of exceptions.ZeroDivisionError object at 0x81710>
    __sizeof__ = <built-in method __sizeof__ of exceptions.ZeroDivisionError object at 0x81710>
    __str__ = <method-wrapper '__str__' of exceptions.ZeroDivisionError object at 0x81710>
    __subclasshook__ = <built-in method __subclasshook__ of type object at 0x5af9c0>
    __unicode__ = <built-in method __unicode__ of exceptions.ZeroDivisionError object at 0x81710>
    args = ('integer division or modulo by zero',)
    message = 'integer division or modulo by zero'

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgitb_log_exception.py", line 25, in <module>
    func1(1, 5)
  File "cgitb_log_exception.py", line 23, in func1
    return func2(a, c)
  File "cgitb_log_exception.py", line 19, in func2
    return a / divisor
ZeroDivisionError: integer division or modulo by zero

HTML Output

Of course, since cgitb was originally developed for handling exceptions in web apps, no discussion would be complete without an example of the HTML output it produces.

import cgitb
cgitb.enable()

def func1(arg1):
    local_var = arg1 * 2
    return func2(local_var)

def func2(arg2):
    local_var = arg2 + 2
    return func3(local_var)

def func3(arg3):
    local_var = arg2 / 2
    return local_var

func1(1)

By leaving out the format argument (or specifying html), the traceback format changes to HTML output.

HTML output example

See also

cgitb
The standard library documentation for this module.
traceback
Standard library module for working with tracebacks.
inspect
The inspect module includes more functions for examining the stack.
sys
The sys module provides access to the current exception value and the excepthook handler invoked when an exception occurs.
Improved traceback module
Python-dev discussion of improvements to the traceback module and related enhancements other developers use locally.

PyMOTW Home

The canonical version of this article


You might also be interested in:

News Topics

Recommended for You

Got a Question?