Skip to main content

Pdb

Python ships with the built-in pdb module. You can use it without installing or importing anything:

Set breakpoints

file.py
# python < 3.7
import pdb
pdb.set_trace()
 
# python 3.7+
breakpoint()

Is this step optional?

Run your app/file (in your terminal)

Run file + stop at first breakpoint():

$ python file.py

Run file and stop at first line (with or without breakpoints):

$ python -m pdb file.py

Run file but ignore all breakpoints:

$ PYTHONBREAKPOINT=0 python file.py

You will see the pdb prompt:

$ python file.py
> /project/file.py(line number)<module>()
-> print(f'path = {filename}')
(Pdb)
  • The line starting with > tells you what file, line number and scope (module or function name) you’re currently in
  • The line starting with -> is where execution is paused has not been run yet
  • The line starting with (Pdb) is the pdb prompt waiting for a command
  • Output starts with --Call-- when you’ve just stepped into a function
  • Output starts with --Return-- when a value is about to be returned
  • Separate “add breakpoints in file + run in terminal” workflow from “run in terminal without having to set any breakpoints in file” workflow?
  • Would the workflow change at all when a debugging test?

Use the debugger (with (Pdb) commands in your terminal):

  • General:
    • <cr> — repeat last command
    • !<expr> — execute python command
    • h — help menu
    • h <command> — help for a command
    • run / restart — restart debugger
    • q / <c-d> — quit debugger
  • Navigate code:
    • “Step over”
      • n — “next”: continue until next logically executed line (without entering function calls) or until current function’s return statement
        • useful for following every iteration of a loop
      • unt — “until”: continue until line ≥ current line in current file (without entering function calls) or until current function’s return statement
        • like n but based only on moving farther down in current file
        • useful for not following every iteration of a loop
      • unt # — “until line”: continue until line ≥ # in current file (without entering functions) or until current function’s return statement
        • Like n + b (or the idea of c #)
    • s — “step into”: continue until next logically executed line (entering called functions) or until current function’s return statement
    • r — “return”: continue until current function’s return statement
    • c — “continue” until next breakpoint
    • j # — “jump” to line number (e.g. to break out of a loop)
  • Navigate stack trace:
    • w — print stack trace with most recent frame at bottom and an arrow indicating the current frame (which determines the context of most commands)
    • u — move up the call stack
      • e.g. commands like pp x may output different values at different points in the stack trace
      • e.g. useful for spotting the function where a value changed unexpectedly
    • d — move down the call stack
    • source <expr> — display source of expr
  • Inspect values:
    • locals — print local variables
    • a — print args of current function
    • p <expr> — print value of expr
      • e.g. p var_1, var_2
    • pp <expr> — pretty-print value of expr
    • whatis <expr> — print type of expr
    • l — list 11 lines around current line
    • ll — long-list all lines in current function
    • l #,# — list lines in range
  • Watch values:
    • display <expr> — display value of expr whenever it changes
      • e.g. useful for watching a variable during a loop:
      • e.g. b # (in loop body) + c + display var + c + <cr>, etc
    • display — list all active display expressions (instead of just the first one)
      • useful after defining a watch list of expressions
      • e.g. b # (in loop body) + c + display var_1 + display var_2 + display var_3 + c + display + c + display, etc
    • undisplay — clear all display expressions
    • undisplay <expr> — stop displaying value of expr
  • Breakpoints:
    • b — list all breakpoints + their numbers
    • b [filename:]lineno — set bp at line in file (defaults to current file)
      • e.g. b util:5
      • e.g. b 5
    • b [filename:]lineno, condition — set bp if condition met (e.g. wrong input)
      • e.g. b util:5, x == 'abc'
      • e.g. b 5, not x.startsWith(’a’)
    • b func_name[, condition] — set bp at first line of function with optional condition
      • condition can only use function args and global variables available when function is entered (not local function variables); otherwise it will fail
      • e.g. b util.get_path
      • e.g. b get_path, not argument.startsWith(’/’)
    • cl # […] — clear bp numbers (separate by spaces)
    • cl filename:lineno — clear all breakpoints at line in file
    • cl — clear all breakpoints (after confirming)
    • disable # — disable bp #
    • enable # — enable bp #
    • ignore # count — ignore bp # count times
    • condition # condition — set/remove condition for bp number (remove if empty)

How to automatically enter debugger when an exception occurs?

Using pdb to write new code

Useful for actually iterating slowly on new code:

  1. Write a few lines
  2. Then write a breakpoint() line
  3. Run the file
  4. Inspect the values via the Pdb prompt (including creating temporary variables to save interesting values) to figure out what to write next
  5. Write a few more lines, set a new breakpoint, and rerun with pdb
  6. Repeat

I do this with console.log when writing JavaScript, but this is a way to do the same with Python.

Research 📚

Inbox