Python debugger: Stepping into a function that you have called interactively
Python is quite cool, but unfortunately, its debugger is not as good as perl -d.
One thing that I do very commonly when experimenting with code is to call a function from within the debugger, and step into that function, like so:
# NOTE THAT THIS PROGRAM EXITS IMMEDIATELY WITHOUT CALLING FOO()
~> cat -n /tmp/show_perl.pl
1 #!/usr/local/bin/perl
2
3 sub foo {
4 print "hi\n";
5 print "bye\n";
6 }
7
8 exit 0;
~> perl -d /tmp/show_perl.pl
Loading DB routines from perl5db.pl version 1.28
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(/tmp/show_perl.pl:8): exit 0;
# MAGIC HAPPENS HERE -- I AM STEPPING INTO A FUNCTION THAT I AM CALLING INTERACTIVELY
DB<1> s foo()
main::((eval 6)[/usr/local/lib/perl5/5.8.6/perl5db.pl:628]:3):
3: foo();
DB<<2>> s
main::foo(/tmp/show_perl.pl:4): print "hi\n";
DB<<2>> n
hi
main::foo(/tmp/show_perl.pl:5): print "bye\n";
DB<<2>> n
bye
DB<2> n
Debugged program terminated. Use q to quit or R to restart,
use O inhibit_exit to avoid stopping after program termination,
h q, h R or h O to get additional info.
DB<2> q
This is incredibly useful when trying to step through a function's handling of various different inputs to figure out why it fails. However, it does not seem to work in either pdb or pydb (I'd show an equivalent python example to the one above but it results in a large exception stack dump).
So my question is twofold:
- Am I missing something?
- Is there a python debugger that would indeed let me do this?
Obviously I could put the calls in the code myself, but I love working interactively, eg. not having to start from scratch when I want to try calling with a slightly different set of arguments.
Solution 1:
And I've answered my own question! It's the "debug" command in pydb:
~> cat -n /tmp/test_python.py
1 #!/usr/local/bin/python
2
3 def foo():
4 print "hi"
5 print "bye"
6
7 exit(0)
8
~> pydb /tmp/test_python.py
(/tmp/test_python.py:7): <module>
7 exit(0)
(Pydb) debug foo()
ENTERING RECURSIVE DEBUGGER
------------------------Call level 11
(/tmp/test_python.py:3): foo
3 def foo():
((Pydb)) s
(/tmp/test_python.py:4): foo
4 print "hi"
((Pydb)) s
hi
(/tmp/test_python.py:5): foo
5 print "bye"
((Pydb)) s
bye
------------------------Return from level 11 (<type 'NoneType'>)
----------------------Return from level 10 (<type 'NoneType'>)
LEAVING RECURSIVE DEBUGGER
(/tmp/test_python.py:7): <module>
Solution 2:
You can interactively debug a function with pdb as well, provided the script you want to debug does not exit() at the end:
$ cat test.py
#!/usr/bin/python
def foo(f, g):
h = f+g
print h
return 2*f
To debug, start an interactive python session and import pdb:
$ python
Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pdb
>>> import test
>>> pdb.runcall(test.foo, 1, 2)
> /Users/simon/Desktop/test.py(4)foo()
-> h = f+g
(Pdb) n
> /Users/simon/Desktop/test.py(5)foo()
-> print h
(Pdb)
The pdb module comes with python and is documented in the modules docs at http://docs.python.org/modindex.html
Solution 3:
There is a python debugger that is part of the core distribution of python called 'pdb'. I rarely use it myself, but find it useful sometimes.
Given this program:
def foo():
a = 0
print "hi"
a += 1
print "bye"
foo()
Here is a session debugging it:
$ python /usr/lib/python2.5/pdb.py /var/tmp/pdbtest.py ~
> /var/tmp/pdbtest.py(2)<module>()
-> def foo():
(Pdb) s
> /var/tmp/pdbtest.py(10)<module>()
-> foo()
(Pdb) s
--Call--
> /var/tmp/pdbtest.py(2)foo()
-> def foo():
(Pdb) s
> /var/tmp/pdbtest.py(3)foo()
-> a = 0
(Pdb) s
> /var/tmp/pdbtest.py(4)foo()
-> print "hi"
(Pdb) print a
0
(Pdb) s
hi
> /var/tmp/pdbtest.py(6)foo()
-> a += 1
(Pdb) s
> /var/tmp/pdbtest.py(8)foo()
-> print "bye"
(Pdb) print a
1
(Pdb) s
bye
--Return--
> /var/tmp/pdbtest.py(8)foo()->None
-> print "bye"
(Pdb) s
--Return--
> /var/tmp/pdbtest.py(10)<module>()->None
-> foo()
(Pdb) s
Solution 4:
For interactive work on code I'm developing, I usually find it more efficient to set a programmatic "break point" in the code itself with pdb.set_trace
. This makes it easir to break on the program's state deep in a a loop, too: if <state>: pdb.set_trace()