Using PDB to debug code
Using PDB to debug code#
Note that a video walkthrough of this PDB tutorial is in Moodle, and will be shown at the start of the lab sessions
In this notebook we will show how to use PDB to debug code. This is an interactive walkthrough, so open in Colab!
See the example below. A code to compute cos(1/x) at a set of values between x=0 to x=10 and then sum all of them (this is actually a basic example of integration done numerically, we’ll explore that more at a later date!)
import numpy
# Specify 1000 values of x between 0 and 10, with delta_x = 0.01
x_vals = numpy.linspace(0,10.,1000)
# Convert to 1 / x
inverse_x = 1./x_vals
# And take the cos of this
cos_inv_x = numpy.cos(1./x_vals)
# To integrate multiply the above by delta_x and sum over all values. This is the very basic
# "rectangular integration". There are more accurate ways to do this, but we're illustrating a problem.
dx = 10 / 1000
sum_vals = numpy.sum(cos_inv_x)
# Finally print the integral
print (sum_vals * dx)
nan
/var/folders/1q/fnnzxxtd1wl4p1pzvyvl_wpm0000gq/T/ipykernel_59380/1868074832.py:5: RuntimeWarning: divide by zero encountered in divide
inverse_x = 1./x_vals
/var/folders/1q/fnnzxxtd1wl4p1pzvyvl_wpm0000gq/T/ipykernel_59380/1868074832.py:7: RuntimeWarning: divide by zero encountered in divide
cos_inv_x = numpy.cos(1./x_vals)
/var/folders/1q/fnnzxxtd1wl4p1pzvyvl_wpm0000gq/T/ipykernel_59380/1868074832.py:7: RuntimeWarning: invalid value encountered in cos
cos_inv_x = numpy.cos(1./x_vals)
Hmmm, some nasty warnings and the code returns NaN. Can this be solved by inspecting the code (Step 1) and by using debug statements (Step 2). Yes, quite probably. But let’s illustrate PDB. You can add the following lines to the code (see cell below). Now when the code runs, it will enter a interactive window after getting to set_trace. From here, you can:
Enter a name of a variable to see what it is set to
Enter n to move to the next line of the code
Enter c to exit interactive mode and continue executing the code
(and other commands which can be found by googling).
Here’s what this looks like when running this and adding some commands. Reproduce this below:
# Illustrating PDB
from IPython.core.debugger import Pdb
import numpy
debug_here = Pdb()
def run_command():
debug_here.set_trace()
x_val = 5.
inv_x = 1./x_val
dx = 10 / 1000
product = inv_x * dx
run_command()
> <ipython-input-39-54f52ffa365c>(5)run_command()
-> x_val = 5.
(Pdb) n
> <ipython-input-39-54f52ffa365c>(6)run_command()
-> inv_x = 1./x_val
(Pdb) x_val
5.0
(Pdb) inv_x
*** NameError: name 'inv_x' is not defined
(Pdb) n
> <ipython-input-39-54f52ffa365c>(7)run_command()
-> dx = 10 / 1000
(Pdb) inv_x
0.2
(Pdb) c
# Illustrating PDB
from IPython.core.debugger import Pdb
import numpy
debug_here = Pdb()
def run_command():
debug_here.set_trace()
x_val = 5.
inv_x = 1./x_val
dx = 10 / 1000
product = inv_x * dx
# PLEASE UNCOMMENT THE LINE BELOW AND RUN THIS!
#run_command()
Here we demonstrated that we are able to check the values of elements in this function and walk through line-by-line. Some notes:
If you are not in a function, the
ncommand might not work well in a Jupyter notebook. You can still pause the code withdebug_here.set_trace()and query values, but you might not be able to move to the next line easily.
However, we saw no problem in what we did here, so let’s look at this more closely:
In the example below we again run through the command. Walk through line by line and identify where the divide-by-zero appears. Can you now see why this code is returning NaN? Is the problem one of implementation, or was the question just ill-defined? Can you explain to other students in the class what is happening here?
from IPython.core.debugger import Pdb
import numpy
debug_here = Pdb()
def run_command():
x_vals = numpy.linspace(0,10.,20)
debug_here.set_trace()
inverse_x = 1./x_vals
cos_inv_x = numpy.cos(1./x_vals)
dx = 10 / 1000
sum_vals = numpy.sum(cos_inv_x)
return (sum_vals * dx)
# AGAIN UNCOMMENT THE LINE BELOW TO RUN THIS!
#print (run_command())