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 #s
    • 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 #s (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 # (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