Debugging

A broken program

We have a Python program called guess.py:

from random import randint

answer = randint(0, 1)
n = input("Guess: 0 or 1? ")

if n == answer:
    print("Correct!")
else:
    print(f"Incorrect. The answer was {answer}.")

We can run this program by typing python, python3, or py (if on Windows) from our system command prompt followed by guess.py (as long as we’re in the same directory as that file):

$ python guess.py

This program is supposed to prompt the user to guess either 0 or 1 and then tell them if their answer was correct.

$ python guess.py

Guess: 0 or 1? 0
Incorrect. The answer was 1.

The problem is that our program always says the answer is incorrect.

$ python guess.py

Guess: 0 or 1? 0
Incorrect. The answer was 0.

breakpoint

Let’s try to investigate what’s going on in this program.

We’ll do this by using the Python Debugger (a.k.a. PDB).

We can start the Python debugger with the built-in breakpoint function. Let’s add a call to breakpoint near the top of our Python program:

from random import randint

breakpoint()

answer = randint(0, 1)
n = input("Guess: 0 or 1? ")

if n == answer:
    print("Correct!")
else:
    print(f"Incorrect. The answer was {answer}.")

Now when we run our program, we’ll see a (Pdb) prompt:

$ python guess.py
> /home/trey/guess.py(5)<module>()
-> answer = randint(0, 1)

We can use the l command to list the file that we’re on:

(Pdb) l
  1     from random import randint
  2
  3     breakpoint()
  4
  5  -> answer = randint(0, 1)
  6     n = input("Guess: 0 or 1? ")
  7
  8     if n == answer:
  9         print("Correct!")
 10     else:
 11         print(f"Incorrect. The answer was {answer}.")

And we can use the n command to run the next line of code:

(Pdb) n
> /home/trey/guess.py(6)<module>()
-> n = input("Guess: 0 or 1? ")

We can also run statements and see their results. Here we’re looking at the value for the answer variable:

(Pdb) answer
1

Let’s run the next line and then respond to the user input prompt:

(Pdb) n
Guess: 0 or 1? 1
> /home/trey/guess.py(8)<module>()
-> if n == answer:
(Pdb)

We have a problem right now. We would like to see the value for the n variable. But n is a PDB command, so if we type n we’ll go to the next line.

We can fix this in a few different ways:

  1. We could put a ! character at the beginning of our line to tell PDB that our statement is a Python statement and not a PDB command

  2. We could run the interact command which would drop us into a regular Python REPL where we can run any commands we’d like as usual

Let’s use interact:

(Pdb) interact
*interactive*
>>>

Now that we’re in a Python REPL we could use the built-in dir function to list all our variables:

>>> dir()
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'answer', 'n', 'randint']

Let’s look at the answer and n variables:

>>> answer
1
>>> n
'1'

Ah hah! The problem is that answer is a number while n is a string.

I’ll leave it up to you to fix our program. We can use exit to exit our program:

>>> exit()

Note

On Python 3.6 and below, the breakpoint function didn’t exist yet. You’ll need to type import pdb ; pdb.set_trace() to start the Python debugger on Python 3.6 and earlier Python versions.

More PDB Commands

Below are the pdb commands I usually use. The characters in parenthesis are optional (so n and next do the same thing):

  • n(ext): Run the next line of code

  • s(tep): Step into the current line of code (step into a function call usually)

  • r(eturn): Return from the current function

  • c(ontinue): Exit PDB, continuing until the next breakpoint or the end of the program

  • l(ist): List the surrounding code lines

  • interact: Enter interactive mode, which starts a Python REPL session