Python DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Interactive Debugging in Python
Pages: 1, 2, 3, 4, 5, 6, 7

The only reason the interpreter would have returned between lines 14 and 18 is that it hit an exception. Because the debugger has a couple of nifty postmortem functions, I modified my script to use one, fired it up to see where it stopped, and started debugging from there. Here is the modified source file; pay special attention to the try/except block in the get_number_dict() method:

#!/usr/bin/env python

import pdb
import string
import sys

class ConvertToDict:
    def __init__(self):
        self.tmp_dict = {}
        self.return_dict = {}
    def walk_string(self, some_string):
        '''walk given text string and return a dictionary. 
        Maintain state in instance attributes in case we hit an exception'''
        l = string.split(some_string)
        for i in range(len(l)):
            key = str(i)
            self.tmp_dict[key] = int(l[i])
        return_dict = self.tmp_dict
        self.return_dict = self.tmp_dict
        self.reset()
        return return_dict
    def reset(self):
        '''clean up'''
        self.tmp_dict = {}
        self.return_dict = {}
    def get_number_dict(self, some_string):
        '''do super duper exception handling here'''
        try:
            return self.walk_string(some_string)
        except:
            #modified exception handler - drop us into a debugger
            tb = sys.exc_info()[2]
            pdb.post_mortem(tb)
            #if we hit an exception, we can rely on tmp_dict 
			being a backup to the point of the exception
            return self.tmp_dict

def main():
    ctd = ConvertToDict()
    for line in file(sys.argv[1]):
        line = line.strip()
        print "*" * 40
        print "line>>", line
        print ctd.get_number_dict(line)
        print "*" * 40
    
if __name__ == "__main__":
    main()

I chose to kick off the postmortem debugger in the except clause of the get_number_dict() method, because get_number_dict() is the closest exception handler to where the exception must be occurring in the walk_string() method. Here is the result of a run with a list immediately after it for context:

jmjones@bean:~/debugger $ python example_debugger_pm.py example_debugger.data
****************************************
line>> 1 2 3 4 5 6 7 8 9 10
{'1': 2, '0': 1, '3': 4, '2': 3, '5': 6, '4': 5, '7': 8, '6': 7, '9': \
    10, '8': 9}
****************************************
****************************************
line>> 1 2 3 4 5 6 7 8 9 10
> /home/jmjones/debugger/example_debugger_pm.py(17)walk_string()
-> self.tmp_dict[key] = int(l[i])
(Pdb) list
 12             '''walk given text string and return a dictionary.
 13             Maintain state in instance attributes in case
                we hit an exception'''
 14             l = string.split(some_string)
 15             for i in range(len(l)):
 16                 key = str(i)
 17  ->             self.tmp_dict[key] = int(l[i])
 18             return_dict = self.tmp_dict
 19             self.return_dict = self.tmp_dict
 20             self.reset()
 21             return return_dict
 22         def reset(self):

This confirmed some of my earlier suspicions. It was hitting an exception in the for loop. Now my goal was to figure out what exception it hit and why:

(Pdb) for e in sys.exc_info(): print "EXCEPTION>>>", e
EXCEPTION>>> exceptions.AttributeError
EXCEPTION>>> Pdb instance has no attribute 'do_for'
EXCEPTION>>> <traceback object at 0x402ef784>

What? My code was not even calling a do_for() method or trying to access any do_for attribute. This is a little gotcha that hounded me for a bit as I was trying to print the last exception from within this example code. You should be able to tell what is going on with a little insight from the traceback module:

(Pdb) import traceback
(Pdb) traceback.print_stack()
  File "example_debugger_pm.py", line 48, in ?
    main()
  File "example_debugger_pm.py", line 44, in main
    print ctd.get_number_dict(line)
  File "example_debugger_pm.py", line 33, in get_number_dict
    pdb.post_mortem(tb)
  File "/usr/local/python24/lib/python2.4/pdb.py", line 1009, in post_mortem
    p.interaction(t.tb_frame, t)
  File "/usr/local/python24/lib/python2.4/pdb.py", line 158, in interaction
    self.cmdloop()
  File "/usr/local/python24/lib/python2.4/cmd.py", line 142, in cmdloop
    stop = self.onecmd(line)
  File "/usr/local/python24/lib/python2.4/cmd.py", line 218, in onecmd
    return self.default(line)
  File "/usr/local/python24/lib/python2.4/pdb.py", line 167, in default
    exec code in globals, locals
  File "<stdin>", line 1, in ?

Basically, the Python interpreter keeps track of any exceptions that running code raises; it saves the last traceback when running code hits an exception. The debugger is just another piece of code that the interpreter runs. When a user feeds the debugger commands, those commands may raise exceptions within the debugger itself. In this case, I gave the debugger a for statement. It first tried to execute a debugger for command by calling the do_for() method. That raised the exception above. Such debugger exceptions can potentially pollute the interpreter with unexpected exceptions and tracebacks that we users don't really want to see when trying to debug our own code. Maybe there is a better way of writing a debugger than combining the debugger code and the debugged code in the same interpreter as shown here, but the Python team has produced a pretty tricky, complex piece of code that otherwise works really well.

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow





Sponsored by: