Python debugger: Stepping into a function that you have called interactively

PythonDebuggingPdb

Python Problem Overview


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:

  1. Am I missing something?
  2. 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.

Python Solutions


Solution 1 - Python

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 - Python

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 - Python

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 - Python

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()

Solution 5 - Python

If you're more familiar with a GUI debugger, there's winpdb ('win' in this case does not refer to Windows). I actually use it on Linux.

On debian/ubuntu:

sudo aptitude install winpdb

Then just put this in your code where you want it to break:

import rpdb2; rpdb2.start_embedded_debugger_interactive_password()

Then start winpdb and attach to your running script.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionygaView Question on Stackoverflow
Solution 1 - PythonygaView Answer on Stackoverflow
Solution 2 - PythonSimonView Answer on Stackoverflow
Solution 3 - PythonJerubView Answer on Stackoverflow
Solution 4 - PythonAlex CoventryView Answer on Stackoverflow
Solution 5 - PythonJeremy CantrellView Answer on Stackoverflow