Interactive Debugging in Python
Pages: 1, 2, 3, 4, 5, 6, 7
This greeted me with a friendly debugger prompt. What next? I always like to
know where I am, so doing a list is a good start:
(Pdb) l
30 except:
31 #if we hit an exception, we can rely on tmp_dict
being a backup to the point of the exception
32 return self.tmp_dict
33
34 def main():
35 -> ctd = ConvertToDict()
36 for line in file(sys.argv[1]):
37 line = line.strip()
38 print "*" * 40
39 print "line>>", line
40 print ctd.get_number_dict(line)
At this point, I could either step through the file a line at a time and examine everything, or formulate a hypothesis and try to prove or disprove it. Formulating a hypothesis also helps to rule out some obvious dead ends. I thought about things I could rule out:
get_number_dict()is not corrupting the input string because it is passing it straight towalk_string().- The input data looked fine. All four lines were white space-separated integers.
What could it be? The problem appears to be happening on either line 1 or 2 and causing problems as far as line 3 of the input file. I wondered about things I might not have watched for:
- Maybe I wasn't resetting
tmp_dictand/orreturn_dictproperly. - Maybe I wasn't walking over the input data properly.
- Maybe I wasn't handling all exceptions properly.
I decided to set break points at lines 14 (the first line of code in the
walk_string() method) and 18 (the first line of code after the
for loop in the walk_string() method), run through
execution of the script once while inspecting variables, and watch for
anomalies.
(Pdb) b 14
Breakpoint 1 at /home/jmjones/debugger/example_debugger.py:14
(Pdb) b 18
Breakpoint 2 at /home/jmjones/debugger/example_debugger.py:18
(Pdb) c
****************************************
line>> 1 2 3 4 5 6 7 8 9 10
> /home/jmjones/svn/home/debugger/example_debugger.py(14)walk_string()
-> l = string.split(some_string)
To get my bearings, I listed where I was:
(Pdb) l
9 self.tmp_dict = {}
10 self.return_dict = {}
11 def walk_string(self, some_string):
12 '''walk given text string and return a dictionary.
13 Maintain state in instance attributes in case
we hit an exception'''
14 B-> 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 B return_dict = self.tmp_dict
19 self.return_dict = self.tmp_dict
You can see I was at line 14, and you can also see the break points at lines
14 and 18. Just out of paranoia, I checked what was in some_string,
even though that printed out after the continue command:
(Pdb) print some_string
1 2 3 4 5 6 7 8 9 10
The continue command dropped me down to the next break point, at
line 18:
(Pdb) c
> /home/jmjones/svn/articles/debugger/example_debugger.py(18)walk_string()
-> return_dict = self.tmp_dict
At this point, self.tmp_dict has everything in it that it is
going to have:
(Pdb) print self.tmp_dict
{'1': 2, '0': 1, '3': 4, '2': 3, '5': 6, '4': 5, '7': 8, '6': 7, '9': \
10, '8': 9}
This looked good. I continued and printed out the next input line:
(Pdb) c
{'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/svn/articles/debugger/example_debugger.py(14)walk_string()
-> l = string.split(some_string)
(Pdb) print some_string
1 2 3 4 5 6 7 8 9 10
The input line looked fine. I continued further, knowing that the debugger should stop at the break point set at line 18. Then I could run through a couple of diagnostics and see whether everything looked OK:
(Pdb) c
{'1': 2, '0': 1, '3': 4, '2': 3, '5': 6, '4': 5, '7': 8, '6': 7}
****************************************
****************************************
line>> 1 2 3 4
> /home/jmjones/svn/articles/debugger/example_debugger.py(14)walk_string()
-> l = string.split(some_string)
Huh? It dropped me back to line 14 rather than on 18, and I was on the next data input line. This means that the interpreter returned before it hit line 18, so it did not fully process the second line of data from the data file. I just found where the problem was, but not what was causing it. I had to run through it again, a little more carefully this time, in order to figure it out. Fortunately, I could extract a little information from this run to help out with the next run.
If walk_string() returned before it hit line 18, it could not have
done a proper reset(). What did self.return_dict and
self.tmp_dict contain just then?
(Pdb) print self.return_dict
{}
(Pdb) print self.tmp_dict
{'1': 2, '0': 1, '3': 4, '2': 3, '5': 6, '4': 5, '7': 8, '6': 7}
That's why the program had too much information in the return dictionary for
data line 3; it carried the dictionary self.tmp_dict with it from
the data line that went bad. I felt pretty confident that the problem occurred
between lines 14 and 18 when processing line 2 of the data file. I just didn't
know why--yet.