- 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
.
s
ource of the problem function, then walk the st
ack into the nested __cause__
of the exception, display the s
ource of the called function, and finally open an i
nterpreter 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_function
andshow_file
commands, 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 thepatdb
prompt and the prompt of any nestedinterpret
ers. 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 theedit
command. 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=d
to replace the defaultj
withd
.- Can require that a chain of keys is pressed by separating them with
+
. For examplePATDB_KEY_DOWN_FRAME=a+b
requires thata
and thenb
be 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
Control
as part of the same key, so that for examplec-x
isControl x
. MeanwhileAlt
is (in the usual way for terminals) treated as a separate key press ofescape
, so that for exampleescape+d
isAlt d
- Overall, for example:
a/c-k+b/escape+l
means that any ofa
, orControl k
followed 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_file
andshow_function
command. The group of keys startingPATDB_KEY_STACK_
correspond to the interactive commands within thestack
command.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_EDITOR
is not available. -
PTPYTHON_CONFIG_HOME
: used by theinterpret
command. This command usesptpython
for 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 p
rinting or i
nteracting 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.