Python. Debugging with pdb

pdb is an interactive debugger for Python that lets you navigate through code while your program is running, inspect and change variable values, step through code line by line (including stepping into nested code), set breakpoints, and perform all the other operations you would expect from a debugger.
When I don’t have PuDB handy and can’t install it, or the display in urwid gets garbled, I use pdb.
The pdb debugger looks similar to gdb.
Using the debugger
Start the debugger like this:
python -m pdb script.py
python -m pdb manage.py runserver 8000
Alternatively, you can specify in your code where the debugger should start:
import pdb; pdb.set_trace()
Start post-mortem debugging like this:
import pdb; pdb.pm()
Post-mortem debugging lets you inspect the program state after an unhandled exception.
You can also call scripts from your code through pdb.run/pdb.runcall, but the startup methods above are more popular.
List of debugger commands
h(elp)— the debugger displays a list of available commands. Runhelp <command>to get help on a specific command;q(uit), exit— exit the debugger.
General commands
l(ist) [<first> [,<last>]]— print the source code. With no arguments, it displays five lines above and five lines below. You can pass two arguments, “first” and “last”, specifying the range of line numbers to display. If you pass just one argument, it displays five lines above and five lines below the specified line number;p <expression>, pp <expression>— “print” and “pprint”, respectively;a(rgs)— display the function’s arguments;whatis <arg>— display the object’s type;alias [<name> [<command> [<parameter>, <parameter> ...]]]— set an alias for a command, for example:(Pdb) alias lv locals().keys() (Pdb) alias gv pp globals().keys()Aliases can contain debugger commands, Python statements and calls to other aliases. Run
aliaswithout arguments to list the defined aliases, or pass an alias name to display its definition.
Refer to alias arguments by position, for example%1for the first argument. Use%*to refer to all arguments. For example:(Pdb) alias dirs !dir(%1) dirs __name__unalias <name>— delete a specific alias;run [<args>...]— reload the script with the specified parameters, for example:(Pdb) run runserver 8001 Restarting ./manage.py with arguments: runserver 8001 > /home/adw0rd/work/project/manage.py(2)() -> import os (Pdb) c Validating models... 0 errors found Django version 1.4.1, using settings 'project.settings' Development server is running at http://127.0.0.1:8001/ Quit the server with CONTROL-C.This way you don’t leave the session, and if you have breakpoints, for instance, you won’t lose them. There is also a built-in alias,
restart, which runsrunwithout arguments;!— a prefix for working directly with the interpreter, such as calling functions or accessing variables. Everything after the prefix is sent to the interpreter for execution. For example:# Когда имя уникально можно делать и так: test_var = 42 # Но когда оно совпадает с именем команды, надо так: !step = 42 (Pdb) step = 42 > /home/adw0rd/work/project/manage.py(3)() -> import sys (Pdb) step > /home/adw0rd/work/project/manage.py(5)() -> if __name__ == "__main__": (Pdb) !step = 42 (Pdb) !step 42
Navigating through code
w(here)— display information about your current position;s(tep)— “step into”: step into a function call if possible; otherwise, move to the next line of code;n(ext)— “step over”: move to the next line of code;unt(il)— move to the next line of code, ensuring that its line number is greater than the current one. For example, if you are at the end of a loop body but this isn’t the last iteration, you won’t be taken back to the beginning of the loop body. Instead, the entire loop will finish executing and the debugger will stop on the next line after the loop, unlike next;r(eturn)— finish (“step out of”) the current function;u(p)— move up one stack frame;d(own)— move down one stack frame;j(ump) <lineno>— jump to the specified line of code without executing the code between the current position and the specified one. Exceptions are for loops and code in a finally block (since it must be executed). Also, you can only jump within the current frame (i.e., the bottom frame).
Breakpoints
From here on, I’ll just call them “breakpoints”, because that’s what I’m used to calling them.
When viewing a listing (the “list” command), lines with breakpoints are marked with a “B” prefix:
(Pdb) b Num Type Disp Enb Where 1 breakpoint keep yes at /home/adw0rd/work/project/manage.py:5 (Pdb) list 1 #!/usr/bin/env python 2 -> import os 3 import sys 4 5 B if __name__ == "__main__": 6 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 7 from django.core.management import execute_from_command_line 8 execute_from_command_line(sys.argv) [EOF]b(reak) ([<file>:]<lineno> | <function>) [<condition>]— set and list breakpoints. The arguments let you specify a file and line number or a function where code execution should stop. For example:(Pdb) b 5 Breakpoint 1 at /home/adw0rd/work/project/manage.py:5 (Pdb) b project/settings.py:10 Breakpoint 2 at /home/adw0rd/work/project/project/settings.py:10 (Pdb) b my_function Breakpoint 3 at /home/adw0rd/work/project/manage.py:15To view the currently set breakpoints, use
breakwithout arguments:(Pdb) b Num Type Disp Enb Where 1 breakpoint keep yes at /home/adw0rd/work/project/manage.py:5 2 breakpoint keep yes at /home/adw0rd/work/project/project/settings.py:10 3 breakpoint keep yes at /home/adw0rd/work/project/manage.py:15Conditional breakpoints deserve a separate look: the breakpoint will trigger only if the condition is true. For example:
# Брейкпоинт выполнится только если переменная "some_var" будет больше 42 (Pdb) b my_function, some_var > 42 Breakpoint 4 at /home/adw0rd/work/project/manage.py:15condition <bpnumber> <str_condition>— add a condition to an existing breakpoint, for example:(Pdb) condition 3 True != Falsetbreak— a temporary breakpoint, deleted after use; it has the same syntax asbreak;ignore <bpnumber> <count>— ignore a specific breakpoint count times. Passing a count of zero resets the ignore count;c(ont(inue))— continue (until the first breakpoint or until the program finishes);disable <bpnumber> [<bpnumber> ...]andenable <bpnumber> [<bpnumber> ...]— the first disables a breakpoint but does NOT remove it from the breakpoint list. The second enables the breakpoint again. For example:(Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at .../script.py:2 2 breakpoint keep yes at .../script.py:12 (Pdb) disable 1 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep no at .../script.py:2 2 breakpoint keep yes at .../script.py:12 (Pdb) enable 1 (Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at .../script.py:2 2 breakpoint keep yes at .../script.py:12cl(ear) (<filename>:<lineno> | [<bpnumber>...])— delete breakpoint(s). If you pass a breakpoint number, only that breakpoint is deleted; if you pass nothing, all breakpoints are deleted. If you pass a path and line number, all breakpoints set at that location are deleted;commands [<bpnumber>]— define additional actions for a breakpoint, such as displaying local variables and so on. For example:(Pdb) break Num Type Disp Enb Where 1 breakpoint keep yes at .../script.py:2 (Pdb) commands 1 (com) p "DEBUG" (com) pp locals() (com) end (Pdb) continue # Тут идёт печать locals()To remove these actions, run
commandsagain, but with an empty body:(Pdb) commands 1 (com) end
Saving configuration settings
To configure pdb, create a file named .pdbrc in the root of your home directory or in the project root. Matching instructions in the project root will override those in the root of your home directory.
Example ~/.pdbrc file:
# Алиас, который позволяет указывать несколько объектов для dir()
# конечно он нужен только для примера...
alias dirs pp [dir(arg) for arg in "%*".split()]
Example ~/work/project/.pdbrc file:
# Пример переопределения алиаса:
alias dirs pp "OVERRIDDEN!"
Integration with Emacs
Many IDEs support pdb, and Emacs is no exception. Just launch it like this:
M+x pdb
You’ll get a pdb console in one pane and a real-time code listing in the other—almost PuDB, but still less convenient.
What else can I read?
The official documentation page, The Python Debugger.
An older article, Using pdb, the Python Debugger (Django Debugging Series, Part 3), and a video:
And the article on Habr, pdb – An Interactive Debugger.
Comments are closed. Read the original discussion in Russian →
