Tutorial for debugging Python code using the commands in the PDB module in Python

  • 2020-04-02 14:44:52
  • OfStack

How many times have you had to change someone else's code? If you're part of a development team, you're in this situation more often than you'd like. However, Python has a neat debugging feature (like most other languages) that is handy to use in this case. This is a quick tutorial that I hope will make your coding life easier.
1. A messy program

For the purpose of this tutorial, let's examine the following simple program.

This program takes two command-line arguments and then performs addition and subtraction.

(assume that the user entered a valid value, so we didn't do error handling in the code.)
 


import sys
def add(num1=0, num2=0):
  return int(num1) + int(num2)
def sub(num1=0, num2=0):
  return int(num1) - int(num2)
def main():
  #Assuming our inputs are valid numbers
  print sys.argv
  addition = add(sys.argv[1], sys.argv[2])
  print addition
  subtraction = sub(sys.argv[1], sys.argv[2])
  print subtraction
if __name__ == '__main__':
  main()

2. PDB

Python provides a useful module, the PDB, which is actually an interactive source code debugger.

You need the following two lines of code to use this module.
 


import pdb
pdb.set_trace()

Take a look at our modified program, which contains some breakpoints.
 


import pdb
import sys
def add(num1=0, num2=0):
  return int(num1) + int(num2)
def sub(num1=0, num2=0):
  return int(num1) - int(num2)
def main():
  #Assuming our inputs are valid numbers
  print sys.argv
  pdb.set_trace() # <-- Break point added here
  addition = add(sys.argv[1], sys.argv[2])
  print addition
  subtraction = sub(sys.argv[1], sys.argv[2])
  print subtraction
if __name__ == '__main__':
  main()

3. Program execution triggers the debugger

Once you set the breakpoint, you can execute the program as usual.
 


python debugger.py 1 2

The program will stop execution at the first breakpoint encountered.
 


['debugger.py']
> /Users/someuser/debugger.py(15)main()
-> addition = add(sys.argv[1], sys.argv[2])
(Pdb)

We set a breakpoint at line 14, so we can see that the next line to execute is line 15. As you can see, the program has stopped before executing to line 15.

We have a few options here, so let's look at some debugging instructions in the following steps.
4. The next line -- > n

In your debugger prompt, type n and run to the next line.
 


> /Users/someuser/debugger.py(14)main()
-> addition = add(sys.argv[1], sys.argv[2])
(Pdb) n
> /Users/someuser/debugger.py(15)main()
-> print addition

This executes the current line of code and prepares the next line.

We could use n to execute the entire program line by line, but that's not really useful.

As you may have seen, the PDB doesn't actually go into our add function. Next, let's look at a few other options to make debugging more interesting.

      Note:
      A cool feature is that you can press the enter key to execute the previous command (in this case, just instruction n).

5. Print - > p

Next, let's start debugging the program again. You can make the PDB jump to the end or until the next breakpoint by clicking c, because there are no more breakpoints in the program, and the program will execute.
 


['debugger.py', '1', '2']
> /Users/someuser/debugger.py(14)main()
-> addition = add(sys.argv[1], sys.argv[2])
(Pdb)

Now, if we want to know what's in sys.argv, we can enter the following:
 


-> addition = add(sys.argv[1], sys.argv[2])
(Pdb) p sys.argv
['debugger.py', '1', '2']
(Pdb) p sys.argv[1]
'1'
(Pdb)

This approach makes it fairly easy to see what values are actually stored in a variable.

Now we're going to go inside the addition function.
Step 6. Single - > s.

We can use "s" to go inside the addition function.


(Pdb) s
--Call--
> /Users/someuser/debugger.py(4)add()
-> def add(num1=0, num2=0):
(Pdb) n
> /Users/someuser/debugger.py(5)add()
-> return int(num1) + int(num2)
(Pdb)

This will take us inside the addition function, where we can now use n, p, and other operational instructions.

Clicking "r" at this point will take us to the previous return statement into the function.

This is useful if you want to quickly jump to the end of a function.
7. Dynamically add breakpoints - > b

Earlier, before the program runs, we set a breakpoint using db.set_trace().

However, often after the debug session has started, we want to add breakpoints at specific points in the program.

Here we can use option "b" to do this.

Let's start the execution again.
 


['debugger.py', '1', '2']
> /Users/someuser/debugger.py(15)main()
-> addition = add(sys.argv[1], sys.argv[2])
(Pdb)

At this point I set a breakpoint at line 18.
 


-> addition = add(sys.argv[1], sys.argv[2])
(Pdb) b 18
Breakpoint 1 at /Users/someuser/debugger.py:18
(Pdb) c
We are in add--
3
> /Users/someuser/debugger.py(18)main()
-> print subtraction
(Pdb) p subtraction
-1
(Pdb)

From above we can see that the PDB jumps to line 18 and waits for the next instruction.

The PDB also assigns a number (in this case, 1) to the breakpoint. For future execution, we can enable or disable the breakpoint by turning on or disabling the breakpoint number.
8 - list > l

Sometimes during debugging, you may forget where you are in your code. In this case, using "l" will print out a friendly summary that shows where you are in the code at the moment.
 


['debugger.py', '1', '2']
> /Users/someuser/debugger.py(15)main()
-> addition = add(sys.argv[1], sys.argv[2])
(Pdb) l
 10
 11   def main():
 12     #Assuming our inputs are valid numbers
 13     print sys.argv
 14     pdb.set_trace() # <-- Break point added here
 15 ->   addition = add(sys.argv[1], sys.argv[2])
 16     print addition
 17     subtraction = sub(sys.argv[1], sys.argv[2])
 18     print subtraction

9. Dynamically assign variables

It's also helpful to know that you can assign variables to help you debug during a debugging session. Such as:
 


['debugger.py', '1', '2']
> /Users/someuser/debugger.py(15)main()
-> addition = add(sys.argv[1], sys.argv[2])
(Pdb) n
We are in add--
> /Users/someuser/debugger.py(16)main()
-> print addition
(Pdb) p addition
3 #<--- addition here is 3
(Pdb) addition = 'this is now string' #<--- We changed the value of additon
(Pdb) n
this is now string #<--- Now when we print it we actually gets it as a string. that we just set above.
> /Users/someuser/debugger.py(17)main()
-> subtraction = sub(sys.argv[1], sys.argv[2])

Note:
If you want to set some variables such as n (that is, the PDB instruction), you should use this instruction:
 


(Pdb) !n=5
(Pdb) p n
5

10. - end > q

Finally, if you want to end debugging anywhere in the code, you can use "q" and the executing program will terminate.
Expand your reading

This article only covers the surface of the PDB usage, use PDB you can actually do more ((link: https://docs.python.org/2/library/pdb.html)).

People using IPython can be found in a (link: https://pypi.python.org/pypi/ipdb) a better debugger, it provides a TAB added, syntax highlighting, and some other cool features.


Related articles: