- The only Python debugger to allow inspecting re-raised or grouped exceptions!
- Inspect frames in a full Python REPL, with syntax highlighting and autocompletion.
- Walk through the whole stack interactively.
- A snappy single-keystroke interface.
- Respects
__tracebackhide__hidden frames. - Usable inside
threading,asyncio, and evenmultiprocessing.
source of the problem function, then walk the stack into the nested __cause__ of the exception, display the source of the called function, and finally open an interpreter inside that frame.
pip install patdb
Set the environment variable PYTHONBREAKPOINT=patdb.debug, and then place a breakpoint() anywhere in your source code. (Or call import patdb; patdb.debug() directly yourself, which is the same thing.)
Pass the --patdb flag, e.g. pytest test_foo.py --patdb, to open the debugger on failure.
When running interactively: call patdb.debug() after the error has returned you to the REPL. (Or breakpoint() if you've set the environment variable as above.)
When running code in a file: python -m patdb foo.py.
When running code on the command line: python -m patdb -c "import foo; foo.problematic_function()".
j: down_frame - Move one frame down.
k: up_frame - Move one frame up.
J: down_callstack - Move one callstack down.
K: up_callstack - Move one callstack up.
s: show_function - Show the current function's source code and interactively set breakpoints.
S: show_file - Show the current file's source code and interactively set breakpoints.
t: stack - Show all frames in all callstacks and interactively scroll through them.
p: print - Pretty-prints the value of an expression.
P: print_long_arrays - Pretty-prints the value of an expression, without summarising arrays.
e: edit - Open the current function in your `$EDITOR`.
i: interpret - Open a Python interpreter in the current frame.
v: visibility - Toggles skipping hidden frames when moving frames or callstacks.
c: continue - Close the debugger and continue the program.
q: quit - Quit the whole Python program.
?: help - Display a list of all debugger commands.
These can be rebound, see 'configuration' below.
Here, a "callstack" refers to all of the frames in the traceback of a single exception, so e.g. J moves down to a nested exception.
The following environment variables are respected:
PATDB_CODE_STYLE
PATDB_EMPH_COLOR
PATDB_ERROR_COLOR
PATDB_INFO_COLOR
PATDB_PROMPT_COLOR
PATDB_DEPTH
PATDB_EDITOR
PATDB_KEY_{command name}, e.g. `PATDB_DOWN_FRAME`
COLORFGBG
EDITOR
PTPYTHON_CONFIG_HOME
-
PATDB_CODE_STYLE: used by theshow_functionandshow_filecommands, as the colour theme for displaying code. Can be the name of any Pygments style, to use as the theme for code. Defaults tosolarized-dark. -
PATDB_EMPH_COLOR: the colour used to emphasise the file/function/lines when displaying location, e.g. inFile *foo.py*, at *some_fn* from *1*, line *2*. Defaults to#4cb066. -
PATDB_ERROR_COLOR: the colour used to display the type of error e.g. in*RuntimeError*: something went wrong. Defaults to#dc322f. -
PATDB_INFO_COLOR: the colour used to displaypatdb:info prompts. Defaults to#888888. -
PATDB_PROMPT_COLOR: the color used to display thepatdb>REPL prompt. Defaults to#268bd2. -
PATDB_DEPTH: controls thepatdbprompt and the prompt of any nestedinterpreters. By default it is justpatdb>and>>>respectively. If you nestpatdb->interpret->patdb->...then this environment variable will increment and successive prompts will appear aspatdb1>,1>>>,patdb2>etc. -
PATDB_EDITOR: used by theeditcommand. If set then this command will call$PATDB_EDITOR $filename $linenumber. If not set then it will fall back to just$EDITOR $filename. -
PATDB_KEY_{command name}: this offers a way to rebind keys. For examplePATDB_KEY_DOWN_FRAME=dto replace the defaultjwithd.- Can require that a chain of keys is pressed by separating them with
+. For examplePATDB_KEY_DOWN_FRAME=a+brequires thataand thenbbe pressed to trigger that command. - Can accept multiple different key bindings for the same command by separating them with a
/. For examplePATDB_KEY_DOWN_FRAME=j/d. - Modifiers keys can be applied following prompt_toolkit syntax. In particular this treats
Controlas part of the same key, so that for examplec-xisControl x. MeanwhileAltis (in the usual way for terminals) treated as a separate key press ofescape, so that for exampleescape+disAlt d - Overall, for example:
a/c-k+b/escape+lmeans that any ofa, orControl kfollowed byb, orAlt l, will trigger that keybind. - The full list of all keys is as follows. The group of keys starting
PATDB_KEY_SHOW_correspond to the interactive commands within theshow_fileandshow_functioncommand. The group of keys startingPATDB_KEY_STACK_correspond to the interactive commands within thestackcommand.PATDB_KEY_DOWN_FRAME PATDB_KEY_UP_FRAME PATDB_KEY_DOWN_CALLSTACK PATDB_KEY_UP_CALLSTACK PATDB_KEY_SHOW_FUNCTION PATDB_KEY_SHOW_FILE PATDB_KEY_STACK PATDB_KEY_PRINT PATDB_KEY_PRINT_LONG_ARRAYS PATDB_KEY_EDIT PATDB_KEY_INTERPRET PATDB_KEY_VISIBILITY PATDB_KEY_CONTINUE PATDB_KEY_QUIT PATDB_KEY_HELP PATDB_KEY_SHOW_DOWN_LINE PATDB_KEY_SHOW_UP_LINE PATDB_KEY_SHOW_LEFT PATDB_KEY_SHOW_RIGHT PATDB_KEY_SHOW_DOWN_CALL PATDB_KEY_SHOW_SELECT PATDB_KEY_SHOW_LEAVE PATDB_KEY_STACK_DOWN_FRAME PATDB_KEY_STACK_UP_FRAME PATDB_KEY_STACK_DOWN_CALLSTACK PATDB_KEY_STACK_UP_CALLSTACK PATDB_KEY_STACK_LEFT PATDB_KEY_STACK_RIGHT PATDB_KEY_STACK_VISIBILITY PATDB_KEY_STACK_ERROR PATDB_KEY_STACK_COLLAPSE_SINGLE PATDB_KEY_STACK_COLLAPSE_ALL PATDB_KEY_STACK_SELECT PATDB_KEY_STACK_LEAVE
- Can require that a chain of keys is pressed by separating them with
-
COLORFGBG: some environments provide this to describe the colour of the terminal. If available, this will be queried to determine if your terminal is using a light or dark background. This is used to determine the fallback colour for any tokens not specified by the code style specified inPATDB_CODE_STYLE. -
EDITOR: used as a fallback ifPATDB_EDITORis not available. -
PTPYTHON_CONFIG_HOME: used by theinterpretcommand. This command usesptpythonfor the interpreter, and we respect any existing configuration you have configured forptpython.
Why the name patdb?
The built-in debugger is called pdb, and my name is Patrick! 😁
Why this (fairly small) set of commands?
patdb was designed for its commands to be almost entirely be about understanding the stack trace. Once you're where you need to be, then hit i to open a REPL and start interacting with the variables directly.
How does patdb differ from pdb/... etc?
We handle nested exceptions; scrolling through the stack; syntax highlighting; interacting with hidden frames; etc etc. (ipdb does offer a couple of those last ones as well.)
patdb largely aims to supersede these debuggers.
We handle nested exceptions. The main difference, though, is that pudb offers a "graphical" interface, displaying information about variables / exceptions / code / etc. in multiple panes. This is a bit of a philosophical choice -- personally I prefer REPL-like interfaces, as these keep a history of all the things I've ever done, which I can go back and check if required. I find this particularly valuable when debugging! But others may prefer the graphical interface of pudb.
Can I customise the interpreter?
Yes! We use ptpython for our nested REPL, and will respect any ptpython configuration you already have.
Why not ipython? The reason is that I found ipython to be pretty unclear to work with. They have all of:
IPython.core.interactiveshell.InteractiveShell
IPython.terminal.interactiveshell.TerminalInteractiveShell
IPython.terminal.embed.InteractiveShellEmbed
and I couldn't easily figure out how to get any of them to operate correctly when embedded in a larger program. In contrast ptpython pretty much worked out-of-the-box!
When using edit, how can I open my $EDITOR to the current line number? (And not just the top of the current file.)
Set PATDB_EDITOR as discussed in 'configuration' above.
How can I access the current exception or the current frame?
When printing or interacting then the current exception is available as __exception__ and the current frame is available as __frame__.
Investigating an existing exception, traceback, or frame object.
Call either patdb.debug(some_exception) or patdb.debug(some_traceback) or patdb.debug(some_frame) if you have a particular exception/traceback/frame that you'd like to investigate. Exceptions may potentially have multiple callstacks (subexceptions via __cause__ and __context__ are checked, with one callstack for each exception), the others will enable debugging of a single callstack.
Setting sys.excepthook.
By default, Python will call sys.excepthook on any exception that reaches the top of your program. In practice we usually recommend using python -m patdb foo.py as the way to open a debugger when an exception occurs, as unfortunately Python offers no easy way to set sys.excepthook from outside a program.
However if you can modify the top of your script, or if you use usercustomize.py, then another way to enable patdb is to set sys.excepthook = patdb.debug before any exceptions hit the top level.