Debugging Python with pdb

Being a great debugger is almost as important as being great at writing the software. I think I spend just as much time debugging programs as I do writing them. pdb (python debugger) is a very powerful tool for interactively debugging the state of your application and inspecting portions of your code.

For the rest of this post we are going to be debugging a file called fib.py with the following code:

# fib.py
def main():
    low = 0
    high = 1
    for i in range(10):
        print high
        new_high = get_new_high(low, high)
        low = high
        high = new_high

def get_new_high(low, high):
    return low + high

if __name__ == '__main__':
    main()

There are many ways to get your application to drop into pdb, the first way is to import pdb in the file you want to debug and put pdb.set_trace() on any lines you want to debug. So we are going to change our file to look like this:

def main():
    import pdb;pdb.set_trace()
    low = 0
    high = 1
    for i in range(10):
        print high
        new_high = get_new_high(low, high)
        low = high
        high = new_high

def get_new_high(low, high):
    return low + high

if __name__ == '__main__':
    main()

You will see it shows you what method its in and the next line its going to run:

python fib.py
> /home/sontek/code/test/fib.py(3)main()
-> low = 0

The second way is to tell the python interpreter to break as soon as it starts your application using the -m argument:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():

You can see it stopped at the very first line in my application and then from there I can inspect arguments, jump to lines of code, and set break points.

To get some extra context while you are debugging a useful command is the 'list' command:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) list
  1  -> def main():
  2         low = 0
  3         high = 1
  4         for i in range(10):
  5             print high
  6             new_high = get_new_high(low, high)
  7             low = high
  8             high = new_high 
  9     
 10     def get_new_high(low, high):
 11         return low + high

You will notice the '->' character combination will show you where you currently are and list will by default show you 11 lines of code. You can also give it a range of line numbers to display by passing it first and last line arguments like this:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) list 1,4
  1  -> def main():
  2         low = 0
  3         high = 1
  4         for i in range(10):

Another useful feature is setting breakpoints. This is similar to putting pdb.set_trace() in your source but allows you to do so without messing with your code. To do this you just type 'break ':

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) break 5
Breakpoint 1 at /home/sontek/code/test/fib.py:5
(Pdb) c
> /home/sontek/code/test/fib.py(5)main()
-> print high
(Pdb) list
  1     def main():
  2         low = 0
  3         high = 1
  4         for i in range(10):
  5 B->         print high
  6             new_high = get_new_high(low, high)
  7             low = high
  8             high = new_high 
  9     
 10     def get_new_high(low, high):
 11         return low + high

I used the 'c' command which is an abbreviation for 'continue' to resume the execution of the program since I told it to break at the first line using -m pdb. You will see when Iisted out the lines of code there is 'B->' next to line 5, letting me know I have a break point there.

To inspect what a the values of a variable is you can use 'p' which is short for 'print'. If you are looking at a variable with a long string in it you might want to use 'pp' for pretty print. You can also inspect all the local variables by typing locals():

(Pdb) locals()
{'high': 1, 'i': 0, 'low': 0}

You can also make it so that pdb will only stop if certain conditions are met by passing it to the break command or using the condition command after you've defined the breakpoint:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) break 5, i > 6
Breakpoint 1 at /home/sontek/code/test/fib.py:5
(Pdb) c
1
1
2
3
5
8
13
> /home/sontek/code/test/fib.py(5)main()
-> print high

using the condition command, I tell it I want to set the condition on the first break point:

(Pdb) break 5
Breakpoint 1 at /home/sontek/code/test/fib.py:5
(Pdb) condition 1 i > 6

If you have existing breakpoints that you want to add a condition to but don't know the number of them you can use break without any arguments and it will list all your breakpoints out:

(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /home/sontek/code/test/fib.py:5

You can turn breakpoints on and off using the 'enable' and 'disable' commands or remove them completely using the 'clear' command.

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) break 5
Breakpoint 1 at /home/sontek/code/test/fib.py:5
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /home/sontek/code/test/fib.py:5
(Pdb) disable 1
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep no    at /home/sontek/code/test/fib.py:5
(Pdb) enable 1
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /home/sontek/code/test/fib.py:5
(Pdb) clear 1
Deleted breakpoint 1
(Pdb) break

Once you are at a breakpoint you will want to move around to inspect your code, you can do this using the 'next' and 'step' commands which can be abbreviated 's' and 'n'. The big difference between next and step is that next will execute function calls and step will go inside them.

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) break 6
Breakpoint 1 at /home/sontek/code/test/fib.py:6
(Pdb) c
1
> /home/sontek/code/test/fib.py(6)main()
-> new_high = get_new_high(low, high)
(Pdb) n
> /home/sontek/code/test/fib.py(7)main()
-> low = high
(Pdb) c
1
> /home/sontek/code/test/fib.py(6)main()
-> new_high = get_new_high(low, high)
(Pdb) s
--Call--
> /home/sontek/code/test/fib.py(10)get_new_high()
-> def get_new_high(low, high):
(Pdb) n
> /home/sontek/code/test/fib.py(11)get_new_high()
-> return low + high

You can see when we used next it just moved to the next line but when we used step it actually went into the get_new_high function.

Instead of working off of line numbers you can also set break points on function names:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) break get_new_high
Breakpoint 1 at /home/sontek/code/test/fib.py:10
(Pdb) c
1
> /home/sontek/code/test/fib.py(11)get_new_high()
-> return low + high
(Pdb) list
  6             new_high = get_new_high(low, high)
  7             low = high
  8             high = new_high 
  9     
 10 B   def get_new_high(low, high):
 11  ->     return low + high
 12     
 13     if __name__ == '__main__':
 14         main()

You can also move up and down testing different values using the 'jump' command:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) b 5
Breakpoint 1 at /home/sontek/code/test/fib.py:5
(Pdb) c
> /home/sontek/code/test/fib.py(5)main()
-> print high
(Pdb) j 4
> /home/sontek/code/test/fib.py(4)main()
-> for i in range(10):
(Pdb) high=3
(Pdb) c
> /home/sontek/code/test/fib.py(5)main()
-> print high
(Pdb) p high
3
(Pdb) list
  1     def main():
  2         low = 0
  3         high = 1
  4         for i in range(10):
  5 B->         print high
  6             new_high = get_new_high(low, high)
  7             low = high
  8             high = new_high 
  9     
 10     def get_new_high(low, high):
 11         return low + high
(Pdb) j 8
> /home/sontek/code/test/fib.py(8)main()
-> high = new_high

You can also go up and down the stack inspecting what the variables were at that time:

python -m pdb fib.py
> /home/sontek/code/test/fib.py(1)<module>()
-> def main():
(Pdb) b 11
Breakpoint 1 at /home/sontek/code/test/fib.py:11
(Pdb) c
1
> /home/sontek/code/test/fib.py(11)get_new_high()
-> return low + high
(Pdb) up
> /home/sontek/code/test/fib.py(6)main()
-> new_high = get_new_high(low, high)
(Pdb) p low
0
(Pdb) down
> /home/sontek/code/test/fib.py(11)get_new_high()
-> return low + high
(Pdb) list
  6             new_high = get_new_high(low, high)
  7             low = high
  8             high = new_high 
  9     
 10     def get_new_high(low, high):
 11 B->     return low + high
 12     
 13     if __name__ == '__main__':
 14         main()

You can inspect what arguments are being passed to a function with the 'args' command:

> /home/sontek/code/test/fib.py(11)get_new_high()
-> return low + high
(Pdb) args
low = 0
high = 1
(Pdb)

You can also use the 'where' command to inspect where in the callstack you currently are.

If you have variables that are named the same as pdb commands like 'n' or 'break' and would like to change them you need to assign them different values in the debugger for testing use an exclamation point before the command that you want to evaluate:

!n=1

Here are some other useful resources on learning pdb:

http://docs.python.org/library/pdb.html

http://www.doughellmann.com/PyMOTW/pdb/