Debugging is one of those things nobody really likes, but is very important to master. It can take hours, and because of Murphy's law, you most likely, don't have that time. Therefore, it is important to be systematic and know your tools well. After you are done finding the bug and implementing a fix, you should have a test in place. This way at least you will not have to go through the hell of debugging again. Unit testing is covered in the next chapter. We will debug the following buggy code, which tries to access an array element that is not present:
import numpy a = numpy.arange(7) print a[8]
The IPython debugger works as the normal Python pdb
debugger; it adds features such as tab completion and syntax highlighting.
The following steps illustrate a typical debugging session:
Start the IPython shell. Run the buggy script in IPython by issuing the following command:
In [1]: %run buggy.py --------------------------------------------------------------------------- IndexError Traceback (most recent call last) .../site-packages/IPython/utils/py3compat.pyc in execfile(fname, *where) 173 else: 174 filename = fname --> 175 __builtin__.execfile(filename, *where) .../buggy.py in <module>() 2 3 a = numpy.arange(7) ----> 4 print a[8] IndexError: index out of bounds
Now that our program has crashed, we can start the debugger. This will set a breakpoint on the line where the error occurred:
In [2]: %debug > .../buggy.py(4)<module>() 2 3 a = numpy.arange(7) ----> 4 print a[8]
We can list code with the
list
command, or use the shorthand l
:
ipdb> list 1 import numpy 2 3 a = numpy.arange(7) ----> 4 print a[8]
We can now evaluate arbitrary code at the current line, the line to which the debugger is currently pointing:
ipdb> len(a) 7 ipdb> print a [0 1 2 3 4 5 6]
The call stack is a stack containing information about active functions of a running program. We can view the call stack with the bt
command:
ipdb> bt .../py3compat.py(175)execfile() 171 if isinstance(fname, unicode): 172 filename = fname.encode(sys.getfilesystemencoding()) 173 else: 174 filename = fname --> 175 __builtin__.execfile(filename, *where) > .../buggy.py(4)<module>() 0 print a[8]
Move up the call stack:
ipdb> u > .../site-packages/IPython/utils/py3compat.py(175)execfile() 173 else: 174 filename = fname --> 175 __builtin__.execfile(filename, *where)
Move down the call stack:
ipdb> d > .../buggy.py(4)<module>() 2 3 a = numpy.arange(7) ----> 4 print a[8]