Use the Python `pdb` Debugger Module

Learn how to use Python's pdb module to set breakpoints, step through code, inspect variables, and debug programs interactively.

6 min read

The pdb module in the Python standard library is Python's built-in interactive debugger. It lets you pause program execution at any point, inspect variable values, step through code line by line, and evaluate expressions in the current context. Use pdb when print-debugging is not enough and you need to explore program state interactively; pair it with cProfile when the question is about speed rather than correctness.

pythonpython
def calculate(a, b):
    result = a * b
    breakpoint()
    return result + 10
 
calculate(3, 5)

Running this script drops you into an interactive pdb session right at the breakpoint() call, with the program paused mid-execution:

texttext
> script.py(4)calculate()
-> return result + 10
(Pdb) p a
3
(Pdb) p b
5
(Pdb) p result
15
(Pdb) c

breakpoint() (built-in since Python 3.7) pauses execution and starts pdb.

p a prints the value of a. p result shows 15 (3 * 5). c continues execution.

Essential pdb commands

Once you are inside a pdb session, these commands cover most debugging needs.

CommandShortDescription
nextnExecute current line, stop at next line in same function
stepsStep into a function call
continuecResume execution until next breakpoint
print(var)p varPrint the value of a variable
pp varppPretty-print a variable
listlShow source code around the current line
wherewShow the call stack
upuMove up one stack frame
downdMove down one stack frame
argsaPrint arguments of the current function
quitqExit the debugger

Here is a debugging session using several commands:

pythonpython
def divide(a, b):
    breakpoint()
    return a / b
 
def main():
    result = divide(10, 0)
    print(result)
 
main()

Execution pauses at the breakpoint inside divide(), where args and p reveal exactly why the upcoming division is about to fail:

texttext
> script.py(3)divide()
-> return a / b
(Pdb) args
a = 10
b = 0
(Pdb) p b
0
(Pdb) n
ZeroDivisionError: division by zero
> script.py(3)divide()
-> return a / b
(Pdb) q

args shows the function parameters. p b confirms b is 0.

n executes the division, which raises ZeroDivisionError. q exits.

Setting breakpoints in pdb

You can set breakpoints while inside a pdb session without editing the source code.

texttext
(Pdb) break 12
Breakpoint 1 at script.py:12
(Pdb) break mymodule.my_function
Breakpoint 2 at mymodule.py:5

break 12 sets a breakpoint at line 12. break mymodule.my_function sets one directly at a function, wherever it happens to be defined.

Once a breakpoint exists, you can make it conditional, list every breakpoint, and remove one you no longer need:

texttext
(Pdb) condition 1 x > 100
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at script.py:12
    stop only if x > 100
2   breakpoint   keep yes   at mymodule.py:5
(Pdb) clear 1
Deleted breakpoint 1

condition 1 x > 100 makes breakpoint 1 conditional. break with no arguments lists all breakpoints. clear 1 removes breakpoint 1.

Debugging from the command line

Run a script under pdb from the start:

bashbash
python -m pdb myscript.py

This starts pdb before the first line of your script. Use c to run until the first breakpoint() or exception.

For post-mortem debugging after an exception, use:

pythonpython
import pdb
import traceback
import sys
 
def main():
    try:
        1 / 0
    except Exception:
        traceback.print_exc()
        pdb.post_mortem(sys.exc_info()[2])

pdb.post_mortem() starts the debugger at the point where the exception was raised, letting you inspect the exact state that caused the failure.

Practical example: debugging a recursive function

Step through a recursive function to understand how it works.

pythonpython
def factorial(n):
    breakpoint()
    if n <= 1:
        return 1
    return n * factorial(n - 1)
 
factorial(3)

The session below starts by checking n and stepping to the recursive call on the next line of the function:

texttext
> script.py(3)factorial()
-> if n <= 1:
(Pdb) p n
3
(Pdb) n
> script.py(5)factorial()
-> return n * factorial(n - 1)

From there, s steps into the recursive call itself, descending one level deeper into a fresh call to factorial() with n one less than before:

texttext
(Pdb) s
--Call--
> script.py(2)factorial()
-> def factorial(n):
(Pdb) p n
2
(Pdb) n
> script.py(3)factorial()
-> if n <= 1:
(Pdb) p n
2

n executes one line. s steps into factorial(n - 1), descending into the recursive call. You can follow the entire call chain and watch n decrease with each recursion level.

Using pdb in production-like environments

Sometimes you cannot add breakpoint() to source code. Start pdb from within a running program:

pythonpython
import pdb
 
def process(data):
    for item in data:
        if item.get("error"):
            pdb.set_trace()
        handle(item)

pdb.set_trace() is the older equivalent of breakpoint(). It still works but breakpoint() is preferred in new code because it respects the PYTHONBREAKPOINT environment variable.

Common mistakes

Leaving breakpoint() in production code. breakpoint() halts execution. Remove or guard all breakpoints before deploying. Use PYTHONBREAKPOINT=0 as a failsafe to disable all breakpoints.

Using p for everything instead of pp. p is fine for simple values. For nested dictionaries, lists, or objects, pp (pretty-print) formats the output for readability.

Not using n vs s correctly. n (next) stays in the current function. s (step) enters function calls. If you accidentally step into a standard library function, use r (return) to run until the function returns.

Forgetting that pdb blocks the program. In multi-threaded or async code, breakpoint() freezes only the current thread. Other threads keep running. Use thread-aware debugging tools for concurrent code.

Rune AI

Rune AI

Key Insights

  • Add breakpoint() (Python 3.7+) to pause execution and enter pdb at that line.
  • n (next) executes the current line and stops at the next line in the same function.
  • s (step) steps into function calls; c (continue) runs until the next breakpoint.
  • p variable prints a value; pp variable pretty-prints it.
  • l (list) shows source code around the current line.
  • q (quit) exits the debugger.
  • Run python -m pdb script.py to debug from the start.
RunePowered by Rune AI

Frequently Asked Questions

Should I use pdb or an IDE debugger?

`pdb` works in any terminal, on any server, and requires no configuration. IDE debuggers like VS Code or PyCharm offer a graphical interface with variable watches and conditional breakpoints that are easier to use. Use `pdb` when you are on a remote server, in a container, or prefer the terminal. Use an IDE debugger for everyday local development.

How do I set a breakpoint in my code?

Add `breakpoint()` on its own line where you want execution to pause. This built-in function (Python 3.7+) drops you into the pdb debugger at that exact point. Remove the line when you are done debugging.

How do I debug an exception after it happens?

Run `python -m pdb script.py` to start under the debugger. When an unhandled exception occurs, pdb stops and lets you inspect the state at the point of failure. You can also call `pdb.pm()` (post-mortem) in a script after catching an exception to inspect the traceback.

Conclusion

pdb is Python's built-in interactive debugger. Use breakpoint() to pause execution at any line, then step through code with n (next), s (step into), and c (continue). Inspect variables with p (print) and pp (pretty-print). For remote debugging or headless servers, pdb is essential. For daily local development, an IDE debugger is more convenient.