Meet pdbp (Pdb+) — SeleniumBase‘s new built-in Python debugger!
Why not just use Python’s built-in pdb debugger or another existing one like ipdb or pdbpp? Starting with
pdb, it has a limited display with no color and limited abilities. Here’s Python’s built-in
ipdb debugger added tab completion, syntax highlighting, better tracebacks, etc. (I talked about it in a past blog post.) Here’s a sample of how it looks:
The major drawback is that it comes with a high number of external dependencies that take time to download. Here’s a pipdeptree for
> pipdeptree ipdb==0.13.13 ├── decorator [required: Any, installed: 5.1.1] └── ipython [required: >=7.31.1, installed: 8.14.0] ├── appnope [required: Any, installed: 0.1.3] ├── backcall [required: Any, installed: 0.2.0] ├── decorator [required: Any, installed: 5.1.1] ├── jedi [required: >=0.16, installed: 0.18.2] │ └── parso [required: >=0.8.0,<0.9.0, installed: 0.8.3] ├── matplotlib-inline [required: Any, installed: 0.1.6] │ └── traitlets [required: Any, installed: 5.9.0] ├── pexpect [required: >4.3, installed: 4.8.0] │ └── ptyprocess [required: >=0.5, installed: 0.7.0] ├── pickleshare [required: Any, installed: 0.7.5] ├── prompt-toolkit [required: >=3.0.30,<3.1.0,!=3.0.37, installed: 3.0.38] │ └── wcwidth [required: Any, installed: 0.2.6] ├── Pygments [required: >=2.4.0, installed: 2.15.1] ├── stack-data [required: Any, installed: 0.6.2] │ ├── asttokens [required: >=2.1.0, installed: 2.2.1] │ │ └── six [required: Any, installed: 1.16.0] │ ├── executing [required: >=1.2.0, installed: 1.2.0] │ └── pure-eval [required: Any, installed: 0.2.2] └── traitlets [required: >=5, installed: 5.9.0]
If you’re already using ipython, this isn’t a problem because you’ll already need to download most of these dependencies anyway. But if you’re not using
ipython… you’ll still need to download those dependencies.
Up next, is the
pdbpp debugger. It has tab completion, syntax highlighting, better exceptions, different modes for displaying code so that you can get a full view vs. line-by-line, etc. Here’s a sample of how it looks:
Here’s what the main issue is:
pdbpp has a dependency on
fancycompleter, which has a Windows dependency on
pyreadline (https://github.com/pyreadline/pyreadline), which has this issue: pyreadline/pyreadline#65, which leads to this error:
AttributeError: module 'collections' has no attribute 'Callable'. This is a major problem for Windows users running Python
3.11 or newer.
Here’s what I did to fix and improve on that: (Using
I created the pdbp (Pdb+) Python debugger with a dependency on my own library
tabcompleter (https://github.com/mdmintz/tabcompleter), which has a dependency on the improved
pyreadline3 (https://github.com/pyreadline3/pyreadline3/) instead of
pyreadline. Then things started working again. As a bonus, I fixed some bugs, improved on default configuration settings, and added some new features.
Not only is it powerful, but it’s light on dependencies. Here’s a pipdeptree for
> pipdeptree pdbp==1.4.6 ├── Pygments [required: >=2.16.1, installed: 2.16.1] └── tabcompleter [required: >=1.2.1, installed: 1.2.1]
Here are a few examples of the new pdbp debugger in action:
Now that we’ve covered the differences between debuggers, let’s cover installation:
pip install pdbp (Note:
pdbp is already included with
import pdbp to an
__init__.py of your project, which will automatically make
Pdb+ the default debugger at breakpoints.
Now let’s talk about using it:
Debug Mode in
pytest can be triggered in a few different ways:
- Your test raises an exception after passing the
- The moment your test begins after passing the
"pdbp.set_trace()"from your test after importing
"pdbp"respectively. (Calling the
"breakpoint()"method also works.)
- At the end of a SeleniumBase test if you added the
When Debug Mode is activated, the browser window will remain open (when used in combination with browser automation), and you can see how variables look from the command-line.
(Note that you may need to add
"-s" to your
"pytest" run command to allow breakpoints, unless you already have a
"pytest.ini" file present with
"addops = --capture=no" in it.)
Once you’re in Debug Mode, there are several commands that you can use in order to control and debug tests:
n (next): Execute the next line of the current method block.
s (step): Step through and execute the next line of the current method block (but if the current method calls another method, go down the stack).
c (continue): Leave Debug Mode and continue the test where the current method left off.
r (return): Continue running the test until the current method returns.
j (jump): Jump to the line number of the current method block.
w (where): Show where you are within the current stack trace.
u (up): Move up the stack.
d (down): Move down the stack.
ll (longlist): See the code for the current method block.
dir(): List namespace objects.
h (help): List all available commands.
Here are some new commands added by
sticky: Toggle between sticky and non-sticky mode, which shows full code blocks vs individual lines in a stack.
trun (truncate): Use this to toggle between truncating or not truncating long lines in sticky mode.
Additionally, you can execute any Python code that you want from within Debug Mode.