Python - Debugger (PDB)



Python provides a built-in module called pdb for debugging Python programs. The pdb module can be used to set breakpoints, move through code, check value of variables, and evaluate expressions. In this chapter, we will explain how to use the pdb module to debug Python programs.

Following topics will be covered in this chapter −

The pdb Module in Python

The pdb module is a built-in Python module that provides an interactive debugging environment for Python programs. Internally, it is defined as a class called Pdb, which is a subclass of the bdb.Bdb class. The main advantage of pdb debugger it will run completely in the terminal, meaning you don't need to install any additional software or IDE to use it.

To start debugging with pdb, first import the module in your Python script −

import pdb

Then, you can set breakpoints in your code using the pdb.set_trace() function. When the program execution reaches a breakpoint, it will pause and enter the pdb debugging terminal.

Basic Commands in pdb Terminal

After entering the debugging terminal, you can use various commands to control the execution of your program. Here are some of the most commonly used pdb commands −

  • n (next) − Execute the next line of code.
  • s (step) − Step into a function call.
  • c (continue) − Continue execution until the next breakpoint.
  • l (list) − List the current location in the code.
  • p (print) − Print the value of a variable.
  • q (quit) − Quit the debugger and exit the program.
  • b (break) − Set a breakpoint at a specified line number or function.

For a complete list of pdb commands, you can use the h command within the pdb interactive environment. It will look like this −

pdb help command

Example of Using pdb Module

The following example shows how pdb can be used to stop the execution of a program at certain points and inspect the values of variables.

import pdb

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

pdb.set_trace()  # Set a breakpoint here
result = add(20, 30)
print("Addition Result:", result)

pdb.set_trace()  # Set another breakpoint here
result = subtract(20, 10)
print("Subtraction Result:", result)

When the program execution reaches the pdb.set_trace() line, it will pause and enter the pdb terminal. So, the output of the program will be −

> /tmp/ipython-input-1706780559.py(11)<cell line: 0>()
      9 x = 10
     10 y = 20
---> 11 pdb.set_trace()  # Set a breakpoint here
     12 result = add(x, y)
     13 print("Addition Result:", result)

<strong>ipdb> c<strong>
Addition Result: 30

<strong>ipdb> c</strong>
Subtraction Result: 10

Explanation − In the output, you can see that at this terminal, we pressed the c command to continue execution until the next breakpoint. So we get addition result and program paused. Again we pressed c to continue execution and print the subtraction result.

Checking Variable Type at Runtime

To check the type of a variable at runtime while using pdb, you can use the whatis command at the pdb prompt. The example below demonstrates how to use the whatis command −

import pdb

a = 10
b = 20.5
c = "Hello, World!"

pdb.set_trace()  # Set a breakpoint here

The output of the program will be −

> /tmp/ipython-input-1057349253.py(7)<cell line: 0>()
      3 a = 10
      4 b = 20.5
      5 c = "Hello, World!"
      6 
----> 7 pdb.set_trace()  # Set a breakpoint here

<strong>ipdb> whatis a</strong>
<class 'int'>
<strong>ipdb> whatis b</strong>
<class 'float'>
<strong>ipdb> whatis c</strong>
<class 'str'>

Post-Mortem Debugging with pdb Module

The post-mortem debugging refers to the process of analyzing a program after it has crashed or reached an exception state. The pdb module can be used to perform post-mortem debugging using the pdb.post_mortem() function. You can call this function inside any exception handler at any point in your code.

The pdb.post_mortem() function takes the traceback object as an argument, you can get it using the sys.exc_info() function. Here is an example that demonstrates how to use the pdb.post_mortem() function for post-mortem debugging −

import pdb
import sys

def calculate_inverse(x):
    print(f"Calculating inverse for {x}:")
    result = 1 / x
    print(result)

try:
    calculate_inverse(10)
    calculate_inverse(0)  # This will raise error

except Exception:
    # This block is executed only after an exception (crash) occurs.
    print("\n An exception occurred! Starting post-mortem debugging... ")

    # Get the traceback object of the exception
    extype, exvalue, tb = sys.exc_info()
    
    # Launch the post-mortem debugger
    pdb.post_mortem(tb)

In this example, the calculate_inverse() function is used to calculate the inverse of zero. This will create a ZeroDivisionError exception. When the exception occurs, the program will enter the except block. Here we call the pdb.post_mortem(tb) function to start the post-mortem debugging session. The output of the program will be −

<strong>Calculating inverse for 10:
0.1
Calculating inverse for 0:</strong>

 An exception occurred! Starting post-mortem debugging... 
> /tmp/ipython-input-1016125975.py(6)calculate_inverse()
      4 def calculate_inverse(x):
      5     print(f"Calculating inverse for {x}:")
----> 6     result = 1 / x
      7     print(result)
      8 

ipdb> p x
0

Explanation − In the output, you can see that the program has paused at the line where the exception occurred. We printed the value of the variable x using the p command. We will get clear idea that the program crashed because we tried to divide by zero.

Managing Breakpoints in pdb Module

We already saw how to set breakpoints using the pdb.set_trace() function. While working on large applications, we often set multiple breakpoints in different parts of the code. So it is important properly manage these breakpoints. Here are few commands that you can use to manage breakpoints in pdb −

  • break − The break command is used to set a breakpoint at any line number from the pdb prompt. For example, break main.py:9 will set a breakpoint at line number 9 in the main.py file.
  • disable − The disable command is used to disable a specific breakpoint by its number. For example, disable 1 will disable the breakpoint with number 1.
  • enable − The enable command is used to enable the breakpoint that was disabled by disable command. For example, enable 1 will enable the breakpoint with number 1.
  • clear − The clear command is used to remove a breakpoint by its number. For example, clear 1 will remove the breakpoint with number 1.

Here is an example that demonstrates how to manage breakpoints in pdb −

import pdb
def multiply(a, b):
    return a * b
def divide(a, b):
    return a / b

pdb.set_trace()             # Set a breakpoint here
result1 = multiply(10, 20)

result2 = divide(10, 0)
print("Multiplication Result:", result1)

print("Division Result:", result2)

In the code, we set a breakpoint using the pdb.set_trace() function. When the program reaches this line, it will pause and enter the pdb session. The output of the program will be −

None
> /tmp/ipython-input-2031724579.py(7)<cell line: 0>()
      5     return a / b
      6 
----> 7 pdb.set_trace()             # Set a breakpoint here

<strong>ipdb> break main.py:8</strong> 
Breakpoint 1 at /tmp/ipython-input-2031724579.py:8
<strong>ipdb> break</strong>
Num Type         Disp Enb Where
1   breakpoint   keep yes at /tmp/ipython-input-2031724579.py:8
<strong>ipdb> disable 1</strong>
Disabled breakpoint 1 at /tmp/ipython-input-2031724579.py:8
<strong>ipdb> enable 1</strong>
Enabled breakpoint 1 at /tmp/ipython-input-2031724579.py:8

Explanation − In the output, you can see that we used the break command at terminal to set a breakpoint at line number 8. Then we displayed the list of breakpoints using the break command without any arguments. After that, we disabled the breakpoint using the disable command and then enabled it again using the enable command.

Conclusion

In this chapter, we learned how to use the pdb module to debug Python programs. We learned the basic commands in pdb, how to set and manage breakpoints, how to check variable types at runtime, and how to perform post-mortem debugging. The debugging tool is very useful for identifying and fixing issues in large code bases. With proper practice, you can easily find and fix bugs in your Python programs.

Advertisements