Debugging Python in pdb and setup breakpoints from Python code

This blog describes some techniques/tips on how to debug Python using the Python debugger pdb.

My "Vim Python IDE"
The following screen shot shows VIM configured as an almost full function Python IDE - there are 3 windows in VIM. I edit source code in the upper left window and open the lower left window for ipython, which shows imported objects in current namespace; finally, I have pdb debugging the source code at the right side window. All these can be done with a single key stroke inside VIM! (click the image to enlarge and click again to maximize it)




First, I'll show how to use basic Python pdb commands to debug Python code; then I'll show how to setup pdb breakpoints from the source code.

Then, I'll list vim key mapping that inserts breakpoints by just pressing F1 in vim and use F5 key to debug Python code without leaving Vim editor. Use this technique, you can debug Python code and travel back-and-forth between vim and pdb quickly.

At the end, I'll list additional vim key mappings (Thanks ConqueShell!!) that enable a single key stroke to trigger a pdb/ipython/ipdb in a split window inside Vim. This will turn your Vim into a full function Python IDE, you can edit and debug source code, evaluate attributes, objects in one vim screen!

Assume I'm working on the follow code and want to debug it:
shan@ub1:~/code$ nl test.py
1 #/usr/bin/env python

2 class Employee:
3 def __init__(self, name, title=None, phone=5551212):
4 self.name = name
5 self.title = title
6 self.phone = phone

7 def employee():
8 shan = Employee("Shan Jing","UNIX ADMIN","2720748")
9 joe = Employee("Joe Smith","Application Developer","7207837")
10 print (shan.name, shan.title, shan.phone)
11 print (joe.name, joe.title, joe.phone)


12 if __name__ == '__main__':
13 employee()

1. Use pdb to debug python (from Python IDLE):

Basic procedures:
a. start python IDLE
b. import pdb module
c. import python-code
d. type pdb.run('module.method()') and you will stop at (Pdb) prompt:

For exmaple:

shan@ub1:~/code$ python
Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb
>>> import test
>>> pdb.run('test.employee()')
> (1)()->None
(Pdb)




Now, type pdb command 's' to step into your code.

(Pdb) s
--Call--
> /home/shan/code/test.py(9)employee()
The line
>/home/shan/code/test.py(9)employee()
means the we stop at code /home/shan/code/test.py line 9 which is employee() method/function.

The "--Call--" indicates we step into a function/method, if you don't want to step into a function but want to execute it, use the 'n' command instead (explained later)

type pdb command 'l' to list source code. If you want to see more lines of code, you can type 'l range' eg 'l 12,22' to display line 12 to 22.


(Pdb) l
4 def __init__(self, name, title=None, phone=5551212):
5 self.name = name
6 self.title = title
7 self.phone = phone
8
9 -> def employee():
10 shan = Employee("Shan Jing","UNIX ADMIN","2720748")
11 joe = Employee("Joe Smith","Application Developer","7207837")
12 print (shan.name, shan.title, shan.phone)
13 print (joe.name, joe.title, joe.phone)
14



Please note, from the above output, the following line shows the NEXT line of code Python is about to execute.

'9 -> def employee():'

When using 's' (step command), it's good to know two other related commands - 'n' and 'c'.

Command 'n' (next) will execute the command one line at a time without stepping into functions.

Command 'c' (continue) will continue to execute until the end of current function.

Another useful pdb command is 'w' (where), it shows how the python compiler gets to the current line. It's very useful if your current position is nested.

Finally, to inspect attribute's value, use p (print) command. Or just type attribute's name.

Here's a complete run:

shan@ub1:~/code$ python
Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb
>>> import test
>>> pdb.run('test.employee()')
> (1)()
(Pdb) s
--Call--
> /home/shan/code/test.py(9)employee()
-> def employee():
(Pdb) l
4 def __init__(self, name, title=None, phone=5551212):
5 self.name = name
6 self.title = title
7 self.phone = phone
8
9 -> def employee():
10 shan = Employee("Shan Jing","UNIX ADMIN","2720748")
11 joe = Employee("Joe Smith","Application Developer","7207837")
12 print (shan.name, shan.title, shan.phone)
13 print (joe.name, joe.title, joe.phone)
14
(Pdb) s
> /home/shan/code/test.py(10)employee()
-> shan = Employee("Shan Jing","UNIX ADMIN","2720748")
(Pdb) s
--Call--
> /home/shan/code/test.py(4)__init__()
-> def __init__(self, name, title=None, phone=5551212):
(Pdb) s
> /home/shan/code/test.py(5)__init__()
-> self.name = name
(Pdb) s
> /home/shan/code/test.py(6)__init__()
-> self.title = title
(Pdb) s
> /home/shan/code/test.py(7)__init__()
-> self.phone = phone
(Pdb) s
--Return--
> /home/shan/code/test.py(7)__init__()->None
-> self.phone = phone
(Pdb) s
> /home/shan/code/test.py(11)employee()
-> joe = Employee("Joe Smith","Application Developer","7207837")
(Pdb) shan.title
'UNIX ADMIN'
(Pdb) print shan.phone
2720748
(Pdb) w
/usr/lib/python2.6/bdb.py(368)run()
-> exec cmd in globals, locals
(1)()
> /home/shan/code/test.py(11)employee()
-> joe = Employee("Joe Smith","Application Developer","7207837")
(Pdb) c
('Shan Jing', 'UNIX ADMIN', '2720748')
('Joe Smith', 'Application Developer', '7207837')

2. Setting breakpoints from Python code

Sometimes, you don't want to go thru the entire code line by line to debug. All you need is to jump into certain point in your code and debug that section only, you can do that by setting up a breakpoint in Python code.

Just add 'import pdb;pdb.set_trace()' at the point where you want to mark as breakpoint then run your code as normal, it will stop at the breakpoint and gives you pdb prompt.

See below line 9:

1 #/usr/bin/env python

2 class Employee:
3 def __init__(self, name, title=None, phone=5551212):
4 self.name = name
5 self.title = title
6 self.phone = phone

7 def employee():
8 shan = Employee("Shan Jing","UNIX ADMIN","2720748")
9 import pdb;pdb.set_trace()
10 joe = Employee("Joe Smith","Application Developer","7207837")
11 print (shan.name, shan.title, shan.phone)
12 print (joe.name, joe.title, joe.phone)


13 if __name__ == '__main__':
14 employee()


shan@ub1:~/code$ python test2.py
> /home/shan/code/test2.py(12)employee()
-> joe = Employee("Joe Smith","Application Developer","7207837")
(Pdb) l
7 self.phone = phone
8
9 def employee():
10 shan = Employee("Shan Jing","UNIX ADMIN","2720748")
11 import pdb;pdb.set_trace()
12 -> joe = Employee("Joe Smith","Application Developer","7207837")
13 print (shan.name, shan.title, shan.phone)
14 print (joe.name, joe.title, joe.phone)
15
16
17 if __name__ == '__main__':
(Pdb) w
/home/shan/code/test2.py(18)()
-> employee()
> /home/shan/code/test2.py(12)employee()
-> joe = Employee("Joe Smith","Application Developer","7207837")
(Pdb) shan.title
'UNIX ADMIN'
(Pdb) c
('Shan Jing', 'UNIX ADMIN', '2720748')
('Joe Smith', 'Application Developer', '7207837')
shan@ub1:~/code$


Assume you are editing your code in Vim editor, wouldn't it be nice if just hit one key to set a breakpoint and hit another key to debug the code without leaving Vim?

Put the following lines in ~/.vimrc

"pdb setting : insert pdb breakpoints
imap <F1> import pdb;pdb.set_trace()
au BufRead *.py nmap <F5> :!python %

Now, at Vim's insert mode, find the place you want to insert a breakpoint, hit F1 key. To debug your code, (save it first) and hit F5 key, you will be stopped at the breakpoint in a shell, once done debugging just type 'exit' to close pdb and go back to vim continue your coding.

If you have conqueshell installed in vim, using the following Vim mappings, you will be able to open ipython and debug your code from a new buffer in a split window on the same screen on vim. Basically, you turn Vim into a dynamic Python/iPython IDE with a full function debugger!

" ConqueShell Mappings
nmap <F1> :ConqueTermSplit bash
nmap <F2> :ConqueTermVSplit bash
nmap <F3> :ConqueTermSplit ipython
nmap <F4> :ConqueTermVSplit ipython
nmap <F6> :execute 'ConqueTermSplit ipython '.expand('%:p')
nmap <F7> :execute 'ConqueTermVSplit ipython '.expand('%:p')
let g:ConqueTerm_EscKey = ''

I have posted my .vimrc in another blog.

If you want to use ipython (which is a much better IDE) and ipdb to debug Python, please read my next blog - Debugging Python with ipython and ipdb.