Welcome, guest | Sign In | My Account | Store | Cart

The standard Python traceback module provides very useful functions to produce useful information about where and why an error occurred. Traceback objects actually contain a great deal more information than the traceback module displays, however. That information can greatly assist in detecting the cause of your error.

Here's an example of an extended traceback printer you might use, followed by a usage example.

Python, 74 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import sys, traceback

def print_exc_plus():
    """
    Print the usual traceback information, followed by a listing of all the
    local variables in each frame.
    """
    tb = sys.exc_info()[2]
    while 1:
        if not tb.tb_next:
            break
        tb = tb.tb_next
    stack = []
    f = tb.tb_frame
    while f:
        stack.append(f)
        f = f.f_back
    stack.reverse()
    traceback.print_exc()
    print "Locals by frame, innermost last"
    for frame in stack:
        print
        print "Frame %s in %s at line %s" % (frame.f_code.co_name,
                                             frame.f_code.co_filename,
                                             frame.f_lineno)
        for key, value in frame.f_locals.items():
            print "\t%20s = " % key,
            #We have to be careful not to cause a new error in our error
            #printer! Calling str() on an unknown object could cause an
            #error we don't want.
            try:                   
                print value
            except:
                print "<ERROR WHILE PRINTING VALUE>"
        
        
if __name__ == '__main__':
    #A simplistic demonstration of the kind of problem this approach can help
    #with. Basically, we have a simple function which manipulates all the
    #strings in a list. The function doesn't do any error checking, so when
    #we pass a list which contains something other than strings, we get an
    #error. Figuring out what bad data caused the error is easier with our
    #new function.
    
    data = ["1", "2", 3, "4"] #Typo: We 'forget' the quotes on data[2]
    def pad4(seq):
        """
        Pad each string in seq with zeros, to four places. Note there
        is no reason to actually write this function, Python already
        does this sort of thing much better.
        Just an example.
        """
        return_value = []
        for thing in seq:
            return_value.append("0" * (4 - len(thing)) + thing)
        return return_value

    #First, show the information we get from a normal traceback.print_exc().    
    try:
        pad4(data)
    except:
        traceback.print_exc()
    print
    print "----------------"
    print

    #Now with our new function. Note how easy it is to see the bad data that
    #caused the problem. The variable 'thing' has the value 3, so we know
    #that the TypeError we got was because of that. A quick look at the
    #value for 'data' shows us we simply forgot the quotes on that item.
    try:
        pad4(data)
    except:
        print_exc_plus()

I use a technique very similar to this in the application I develop. Unexpected errors get logged in a format like this, which makes it a lot easier to figure out what's gone wrong.

2 comments

Jim Eggleston 17 years, 6 months ago  # | flag

This can be used to create a generic debug script. I wrote something similar to this, but this is nicer. I wrapped it in a separate script called debug.py. debug.py takes a parameter which is the script you wish to debug:

import sys, traceback

def print_exc_plus():
    ...

try:
    execfile(sys.argv[1])
except:
    print_exc_plus()

Then you can run debug.py myscript.py

This is useful for extending editors. e.g. I assign this to F7 in SciTE.

Leave Me Alone 17 years, 3 months ago  # | flag

Code to get frames is poor. The code that gets the frames is poor and will fail for certain types of frames. It is also way too complicated and long. Start the function like this and it will on all versions of Python with all types of frame.

tb = sys.exc_info()[2]
stack = []

while tb:
    stack.append(tb.tb_frame)
    tb = tb.tb_next

traceback.print_exc()
print "Locals by frame, innermost last"