Python

Python PDB Args: Debug a Script With python -m pdb

Run Python pdb with script arguments using python3 -m pdb script.py arg1 arg2. See exact command examples, common mistakes, and essential pdb commands.

Run python -m pdb With Script Arguments

If your script normally takes command-line arguments, put those arguments after the script name when you start pdb:

python3 -m pdb script.py arg1 arg2

For example, this runs calculate_total.py in pdb and still passes --env test --debug to the script:

python3 -m pdb calculate_total.py --env test --debug

The important rule is simple:

python3 -m pdb <your_script.py> <your_script_arguments>

Do not put the script arguments before the script name. Those arguments need to be passed to your script, not to pdb itself.

Once the debugger starts, use commands like:

CommandUse it for
args or aShow arguments for the current function call.
nRun the next line without stepping into a function.
sStep into a function call.
cContinue until the next breakpoint or program exit.
p variable_namePrint a variable.
pp variable_namePretty print a variable.
qQuit pdb.

Quick Example: Debug a Script That Takes Flags

Suppose your script is usually run like this:

python3 calculate_total.py --env test --debug

Start the same script under pdb like this:

python3 -m pdb calculate_total.py --env test --debug

When pdb opens, you can step through the script with n, inspect values with p, or continue with c. The --env test --debug flags are still available to your script through sys.argv or your argument parser.

Introduction To Python's pdb Debugger

pdb is Python's built-in debugger. It helps you pause a script, step through code line by line, inspect variable values, evaluate expressions, and understand why your program is behaving a certain way.

This guide starts with the most common search problem: running a Python script in pdb while still passing command-line arguments. Then it covers the everyday pdb commands you will use when debugging Python scripts, QA automation checks, or test utilities.

What Is pdb?

The Python debugger, pdb, provides an interactive debugging environment for Python programs. It allows you to execute programs step by step, inspect variable values, and understand program execution flow, which is crucial for diagnosing and solving complex bugs.

Getting Started With pdb

To use pdb, start by inserting a breakpoint in your code. A breakpoint tells Python to pause execution at that point and launch the debugger.

If you are using Python 3.7 or newer, add the breakpoint() function at the line where you want the script to pause.

print("Hello World!")
breakpoint()
print("Thank you!")

Before Python 3.7, import pdb and use the set_trace() function.

print("Hello World!")
import pdb; pdb.set_trace()
print("Thank you!")

Execution stops where you put the breakpoint. Then you can interact with the code to get information like variable values, test functions, and inspect program state.

Key pdb Commands

Before diving into the example scripts, review the essential pdb commands used to control execution and inspect programs.

CommandWhat It Does
breakpoint()Inserts a breakpoint where the debugger pauses execution.
print() or pPrints the value of a variable or expression.
ppPretty prints the value of a variable or expression in a more readable format.
continue or cContinues execution until the next breakpoint is encountered.
list or lDisplays lines of code around the current line being executed.
next or nExecutes the next line of code without stepping into functions.
step or sSteps into the function call at the current line.
args or aDisplays the arguments of the current function call.
quit or qQuits the debugger and ends the program.

You can get a complete list of debugger commands from the official Python documentation.

Step By Step Example

This example consists of two Python files: main.py and helpers.py. These scripts simulate a common scenario where data is gathered, processed, and displayed. The example shows how pdb can be used in a realistic debugging situation.

helpers.py

def gather_data():
    return {
        "name": "Jane Doe",
        "age": 28,
        "city": "New York",
        "country": "USA",
        "enrolled": True,
    }


def process_data(data):
    data["processed"] = True
    return data

main.py

from helpers import gather_data, process_data


def main():
    data = gather_data()
    breakpoint()  # Start debugging here
    processed_data = process_data(data)
    print("Original Data:", data)
    print("Processed Data:", processed_data)


if __name__ == "__main__":
    main()

When the main.py script is run, it pauses on the breakpoint. The prompt shows (Pdb).

admas@/.../python-debugger-pdb : python3 main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb)

Using The p And pp Commands

The most common and useful debugging action is printing the value of a variable. In this example, the variable data is defined before the breakpoint, so we can print its value while the debugger is paused.

You can use print(), the p command for printing, or the pp command for pretty printing. My go-to is pp because it prints in a more readable format, especially when dealing with dictionaries, lists, or nested structures.

Here is what it looks like using print() and p.

admas@/.../python-debugger-pdb : python3 main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) print(data)
{'name': 'Jane Doe', 'age': 28, 'city': 'New York', 'country': 'USA', 'enrolled': True}
(Pdb) p data
{'name': 'Jane Doe', 'age': 28, 'city': 'New York', 'country': 'USA', 'enrolled': True}
(Pdb)

Here is what it looks like using pp.

admas@/.../python-debugger-pdb : python3 main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) pp data
{'age': 28,
 'city': 'New York',
 'country': 'USA',
 'enrolled': True,
 'name': 'Jane Doe'}
(Pdb)

The pp command improves readability, especially for nested or complex data structures. It organizes the data in a structured format, which helps you quickly understand the data and its relationships.

Continuing Execution With c

When you are done debugging and want the script to continue, type c and press Enter. The c command means continue.

If there is no other breakpoint, the script runs until the end. If there is another breakpoint, it runs until the next breakpoint is hit.

admas@/.../python-debugger-pdb : python3 main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) c
Original Data: {'name': 'Jane Doe', 'age': 28, 'city': 'New York', 'country': 'USA', 'enrolled': True, 'processed': True}
Processed Data: {'name': 'Jane Doe', 'age': 28, 'city': 'New York', 'country': 'USA', 'enrolled': True, 'processed': True}
admas@/.../python-debugger-pdb :

The c command lets you move from one debugging checkpoint to another, or complete the execution once you have finished your checks.

Using The l Command

The l command means list. It shows lines of code around the line currently being executed.

os/python-debugger-pdb/main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) l
  1     from helpers import gather_data, process_data
  2
  3     def main():
  4         data = gather_data()
  5         breakpoint()
  6  ->     processed_data = process_data(data)
  7         print("Original Data:", data)
  8         print("Processed Data:", processed_data)
  9
 10
 11     if __name__ == "__main__":
(Pdb)

The arrow, ->, tells you the next line that will be executed. In this case, line 6 will be executed next.

Using The n Command

The n command means next. It executes the next line of code without stepping into any functions. This is also called step over.

In this example, n executes process_data(data) without going into the function, then pauses on the next line.

os/python-debugger-pdb/main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) l
  1     from helpers import gather_data, process_data
  2
  3     def main():
  4         data = gather_data()
  5         breakpoint()
  6  ->     processed_data = process_data(data)
  7         print("Original Data:", data)
  8         print("Processed Data:", processed_data)
  9
 10
 11     if __name__ == "__main__":
(Pdb) n
> /Users/admas/Demos/python-debugger-pdb/main.py(7)main()
-> print("Original Data:", data)
(Pdb)

If you want to execute one line at a time, keep entering n.

> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) n
> /Users/admas/Demos/python-debugger-pdb/main.py(7)main()
-> print("Original Data:", data)
(Pdb) n
Original Data: {'name': 'Jane Doe', 'age': 28, 'city': 'New York', 'processed': True}
> /Users/admas/Demos/python-debugger-pdb/main.py(8)main()
-> print("Processed Data:", processed_data)
(Pdb) n
Processed Data: {'name': 'Jane Doe', 'age': 28, 'city': 'New York', 'processed': True}
--Return--
> /Users/admas/Demos/python-debugger-pdb/main.py(8)main()->None
-> print("Processed Data:", processed_data)
(Pdb)

Using The s Command

The s command means step. It steps into the function call at the current line. The difference between n and s is that n executes the next function without entering it, while s steps into the next function.

This is useful when you need to dive into a function implementation to trace a deeper issue or understand the flow inside the function.

admas@/.../python-debugger-pdb : python3 main.py
> /Users/admas/Demos/python-debugger-pdb/main.py(6)main()
-> processed_data = process_data(data)
(Pdb) s
--Call--
> /Users/admas/Demos/python-debugger-pdb/helpers.py(9)process_data()
-> def process_data(data):
(Pdb) s
> /Users/admas/Demos/python-debugger-pdb/helpers.py(14)process_data()
-> data['processed'] = True
(Pdb)

When you enter s, the debugger goes inside the process_data(data) function. When you enter n, it executes process_data(data) but does not step into it.

Conclusion

Mastering Python's pdb debugger is important for developers who want to improve their debugging skills. pdb lets you move through code precisely, set breakpoints, inspect application state, and understand program flow. By using commands like p, pp, c, l, n, and s, you can identify bugs faster and understand how your Python code actually executes.

How do I pass arguments when running python -m pdb?

Use python3 -m pdb script.py arg1 arg2. The arguments after script.py are passed to your script while PDB starts the script in debug mode.

Python PDB Args: Debug a Script With python -m pdb | SuperSQA