pdb++, a drop-in replacement for pdb
Project description
pdb++, a drop-in replacement for pdb
What is it?
This module is an extension of the pdb module of the standard library. It is meant to be fully compatible with its predecessor, yet it introduces a number of new features to make your debugging experience as nice as possible.
pdb++ features include:
colorful TAB completion of Python expressions (through fancycompleter)
optional syntax highlighting of code listings (through Pygments)
several new commands to be used from the interactive (Pdb++) prompt
smart command parsing (hint: have you ever typed r or c at the prompt to print the value of some variable?)
additional convenience functions in the pdb module, to be used from your program
pdb++ is meant to be a drop-in replacement for pdb. If you find some unexpected behavior, please report it as a bug.
Installation
$ pip install pdbpp
Usage
Note that the module is called pdb.py so that pdb++ will automatically be used in all places that do import pdb (e.g. pytest --pdb will give you a pdb++ prompt).
The old pdb module is still available by doing e.g. import pdb; pdb.pdb.set_trace().
New interactive commands
The following are new commands that you can use from the interactive (Pdb++) prompt.
- sticky [start end]
Toggle sticky mode. When in this mode, every time the current position changes, the screen is repainted and the whole function shown. Thus, when doing step-by-step execution you can easily follow the flow of the execution. If start and end are given, sticky mode is enabled and only lines within that range (extremes included) will be displayed.
- longlist (ll)
List source code for the current function. Different from the normal pdb list command, longlist displays the whole function. The current line is marked with ->. In case of post-mortem debugging, the line which actually raised the exception is marked with >>. If the highlight config option is set and Pygments is installed, the source code is highlighted.
- interact
Start an interactive interpreter whose global namespace contains all the names found in the current scope.
- display EXPRESSION
Add an expression to the display list; expressions in this list are evaluated at each step, and printed every time its value changes. WARNING: since these expressions are evaluated multiple times, make sure not to put expressions with side-effects in the display list.
- undisplay EXPRESSION:
Remove EXPRESSION from the display list.
- source EXPRESSION
Show the source code for the given function/method/class.
- edit EXPRESSION
Open the editor in the right position to edit the given function/method/class. The editor used is specified in a config option.
- hf_unhide, hf_hide, hf_list
Some frames might be marked as “hidden” by e.g. using the @pdb.hideframe function decorator. By default, hidden frames are not shown in the stack trace, and cannot be reached using up and down. You can use hf_unhide to tell pdb to ignore the hidden status (i.e., to treat hidden frames as normal ones), and hf_hide to hide them again. hf_list prints a list of hidden frames. The config option enable_hidden_frames can be used to disable handling of hidden frames in general.
Smart command parsing
By default, pdb tries hard to interpret what you enter at the command prompt as one of its builtin commands. However, this is inconvenient if you want to just print the value of a local variable which happens to have the same name as one of the commands. E.g.:
(Pdb) list 1 2 def fn(): 3 c = 42 4 import pdb;pdb.set_trace() 5 -> return c (Pdb) c
In the example above, instead of printing 42 pdb interprets the input as the command continue, and then you lose your prompt. It’s even worse than that, because it happens even if you type e.g. c.__class__.
pdb++ fixes this unfriendly (from the author’s point of view, of course :-)) behavior by always preferring the variable in scope, if it exists. If you really want to execute the corresponding command, you can prefix it with !!. Thus, the example above becomes:
(Pdb++) list 1 2 def fn(): 3 c = 42 4 import pdb;pdb.set_trace() 5 -> return c (Pdb++) c 42 (Pdb++) !!c
Note that the “smart” behavior takes place only when there is ambiguity, i.e. if there exists a variable with the same name as a command: in all other cases, everything works as usual.
Regarding the list command itself, using list(… is a special case that gets handled as the Python builtin:
(Pdb++) list([1, 2]) [1, 2]
Additional functions in the pdb module
The pdb module that comes with pdb++ includes all the functions and classes that are in the module from the standard library. If you find any difference, please report it as a bug.
In addition, there are some new convenience functions that are unique to pdb++.
- pdb.xpm()
eXtended Post Mortem: it is equivalent to pdb.post_mortem(sys.exc_info()[2]). If used inside an except clause, it will start a post-mortem pdb prompt from the line that raised the exception being caught.
- pdb.disable()
Disable pdb.set_trace(): any subsequent call to it will be ignored.
- pdb.enable()
Re-enable pdb.set_trace(), in case it was disabled by pdb.disable().
- @pdb.hideframe
A function decorator that tells pdb++ to hide the frame corresponding to the function. Hidden frames do not show up when using interactive commands such as up, down or where, unless hf_unhide is invoked.
- @pdb.break_on_setattr(attrname, condition=always)
class decorator: break the execution of the program every time the attribute attrname is set on any instance of the class. condition is a callable that takes the target object of the setattr and the actual value; by default, it breaks every time the attribute is set. E.g.:
@break_on_setattr('bar') class Foo: pass f = Foo() f.bar = 42 # the program breaks here
If can be used even after the class has already been created, e.g. if we want to break when some attribute of a particular object is set:
class Foo: pass a = Foo() b = Foo() def break_if_a(obj, value): return obj is a break_on_setattr('bar', condition=break_if_a)(Foo) b.bar = 10 # no break a.bar = 42 # the program breaks here
This can be used after pdb.set_trace() also:
(Pdb++) import pdb (Pdb++) pdb.break_on_setattr('tree_id')(obj.__class__) (Pdb++) continue
Configuration and customization
To customize pdb++, you can put a file named .pdbrc.py in your home directory. The file must contain a class named Config inheriting from pdb.DefaultConfig and override the desired values.
The following is a list of the options you can customize, together with their default value:
- prompt = '(Pdb++) '
The prompt to show when in interactive mode.
- highlight = True
Highlight line numbers and the current line when showing the longlist of a function or when in sticky mode.
- encoding = 'utf-8'
File encoding. Useful when there are international characters in your string literals or comments.
- sticky_by_default = False
Determine whether pdb++ starts in sticky mode or not.
- line_number_color = pdb.Color.turquoise
The color to use for line numbers. See Notes on color options.
- filename_color = pdb.Color.yellow
The color to use for file names when printing the stack entries. See Notes on color options.
- current_line_color = "39;49;7"
The SGR parameters for the ANSI escape sequence to highlight the current line. The default uses the default foreground (39) and background (49) colors, inversed (7). See Notes on color options.
- editor = None
The command to invoke when using the edit command. By default, it uses $EDITOR if set, else vim or vi (if found). If only the editor command is specified, the emacs and vi notation will be used to specify the line number: COMMAND +n filename. It’s otherwise possible to use another syntax by using the placeholders {filename} and {lineno}. For example with sublime text, specify editor = "subl {filename}:{lineno}".
- truncate_long_lines = True
Truncate lines which exceed the terminal width.
- enable_hidden_frames = True
Certain frames can be hidden by default. If enabled, the commands hf_unhide, hf_hide, and hf_list can be used to control display of them.
- show_hidden_frames_count = True
If enable_hidden_frames is True this controls if the number of hidden frames gets displayed.
- def setup(self, pdb): pass
This method is called during the initialization of the Pdb class. Useful to do complex setup.
- show_traceback_on_error = True
Display tracebacks for errors via Pdb.error, that come from Pdb.default (i.e. the execution of an unrecognized pdb command), and are not a direct cause of the expression itself (e.g. NameError with a command like doesnotexist).
With this option disabled only *** exception string gets printed, which often misses useful context.
- show_traceback_on_error_limit = None
This option sets the limit to be used with traceback.format_exception, when show_traceback_on_error is enabled.
Options relevant for source code highlighting (using Pygments)
- use_pygments = None
By default Pygments is used for syntax highlighting of source code when it can be imported, e.g. when showing the longlist of a function or when in sticky mode.
pygments_formatter_class = None
You can configure the Pygments formatter to use via the pygments_formatter_class config setting as a string (dotted path). This should be one of the following typically: "pygments.formatters.Terminal256Formatter", "pygments.formatters.TerminalTrueColorFormatter", or "pygments.formatters.TerminalFormatter".
The default is to auto-detect the best formatter based on the $TERM variable, e.g. it uses Terminal256Formatter if the $TERM variable contains “256color” (e.g. xterm-256color), but also knows about e.g. “xterm-kitty” to support true colors (TerminalTrueColorFormatter). TerminalFormatter gets used as a fallback.
pygments_formatter_kwargs = {}
A dictionary of keyword arguments to pass to the formatter’s constructor.
The default arguments (updated with this setting) are:
{ "style": "default", "bg": self.config.bg, "colorscheme": self.config.colorscheme, } ``style = 'default'`` The style to use, can be a string or a Pygments Style subclass. E.g. ``"solarized-dark"``. See http://pygments.org/docs/styles/. ``bg = 'dark'`` Selects a different palette for dark/light backgrounds. Only used by ``TerminalFormatter``. ``colorscheme = None`` A dictionary mapping token types to (lightbg, darkbg) color names or ``None`` (default: ``None`` = use builtin colorscheme). Only used by ``TerminalFormatter``.
Example:
class Config(pdb.DefaultConfig): pygments_formatter_class = "pygments.formatters.TerminalTrueColorFormatter" pygments_formatter_kwargs = {"style": "solarized-light"}
Notes on color options
The values for color options will be used inside of the SGR escape sequence \e[%sm where \e is the ESC character and %s the given value. See SGR parameters.
The following means “reset all colors” (0), set foreground color to 18 (48;5;18), and background to 21: "0;48;5;18;38;5;21".
Constants are available via pdb.Color, e.g. pdb.Color.red ("31;01"), but in general any string can be used here.
Coding guidelines
pdb++ is developed using Test Driven Development, and we try to keep test coverage high.
As a general rule, every commit should come with its own test. If it’s a new feature, it should come with one or many tests which exercise it. If it’s a bug fix, the test should fail before the fix, and pass after.
The goal is to make refactoring easier in the future: if you wonder why a certain line of code does something, in principle it should be possible to comment it out and see which tests fail.
In exceptional cases, the test might be too hard or impossible to write: in such cases it is fine to do a commit without a test, but you should explain very precisely in the commit message why it is hard to write a test and how to reproduce the buggy behavior by hand.
It is fine NOT to write a test in the following cases:
typos, documentation, and in general any non-coding commit
code refactorings which do not add any feature
commits which fix an already failing test
commits to silence warnings
purely cosmetic changes, such as change the color of the output
CHANGELOG
Changes between 0.10.2 and 0.10.3
Minor bugfix release, moving Continuous Integration from Travis/AppVeyor to GitHub Actions, based on changes on master, but without the (more invasive) (test) fixes for Windows.
Fixes
Fix hideframe decorator for Python 3.8+ (#263)
Fix hidden_frames discrepancy with IPython (#426)
Misc
ci: move to GitHub Actions (#444, #445)
ci: use .coveragerc (#304)
qa/flake8 fixes
test fix for newer PyPy3
Changes between 0.10.0 and 0.10.2
Minor bugfix release to make pdb++ work better with a system-wide installation from within a virtualenv.
Fixes
Fix pth hack: skip if added already (#297)
Version 0.10.2 fixes only the deployment configuration.
Changes between 0.9.15 and 0.10.0
Changes - config: improve current_line_color: default fg/bg/inversed (#188) - Show tracebacks for errors with interactively run code (#185)
This adds new config settings “show_traceback_on_error” (default: True), and “show_traceback_on_error_limit” (default: None).
Fixes
Fix get_stack for non-last index (#192)
This fixes a pretty nasty bug, which was triggered e.g. with the “next” command running into a deeper exception, and the internal stack getting confused (if hidden frames are involved).
Misc:
doc: document enable_hidden_frames, show_hidden_frames_count (#191)
Make break_on_setattr not rely on @hideframe (#190)
tests: cover sticky mode, small improvements (#177)
doc: correct default value for disable_pytest_capturing
Changes between 0.9.14 and 0.9.15
Fixes
Fix completion after debug command (#183)
Misc
tests: fix test_syntaxerror_in_command for Python 3.7.3 (#182)
setup.py: cleanup, testing extra (#181)
test_help: fix restoring of sys.stdout (#180)
tests: move pytest config to setup.cfg, use –pdbcls=pdb:pdb.Pdb (#179)
tests: fix put tests on py27 with user pdbrc (#178)
Improve tests (#176)
Provide message/error methods for py27 (#175)
Changes between 0.9.13 and 0.9.14
Fixes
Fix using the debug command multiple times (#174)
Fix backport for bpo-31078 (keeping use_rawinput) (#169)
Features
new config attribute: default_pdb_kwargs, which can be used to pass default keyword arguments for the Pdb class (#167, #173) This can be used to e.g. pass skip=["importlib._bootstrap_external"].
Misc
test_python_m_pdb_uses_pdbpp: set HOME to tmpdir (#172)
interaction: expand expression to call _cmdloop/cmdloop (#171)
tests: restore sys.gettrace after test, for more coverage (#170)
Changes between 0.9.12 and 0.9.13
Fixes
Fix crash in is_skipped_module with no module name (#163)
Features
Optimize get_stack for show_hidden_frames (#162)
Do not consider the frame with set_trace() as hidden (#161)
Misc
DefaultConfig: make it a new-style class (#166)
Setup coverage reporting (#164)
Changes between 0.9.11 and 0.9.12
Fixes
Fix forget(): check self.lineno, not GLOBAL_PDB.lineno (#160)
do_debug: use func.__globals__ with rebind_globals (#159)
Use super() (#145)
Misc
tests: set_trace: allow to pass in Pdb class (#158)
Changes between 0.9.8 and 0.9.11
Fixes
Make wrapper compatible with python2’s sys.stdout (#155) This was broken since 0.9.4/0.9.5.
- NOTE: 0.9.9 and 0.9.10 were not released to PyPI due to Travis CI
misconfiguration.
Changes between 0.9.7 and 0.9.8
Fixes
interaction: fix ValueError (“signal only works in main thread”) (#143)
rebind_globals: update globals() (#151)
Fix do_debug for py38 (#150)
Misc
do_debug: use PdbppWithConfig class (#147)
tests: harden test_debug_with_overridden_continue (#146)
tests: add test_signal_in_nonmain_thread_with_continue
tests: fail if there is anything on stderr
ci: Travis: add Python 3.8-dev
Changes between 0.9.6 and 0.9.7
Fixes
do_debug: fix setting of use_rawinput (#141)
Changes between 0.9.5 and 0.9.6
Fixes
do_debug: handle SyntaxError (#139)
parseline: handle f-strings with py36 (#138)
Changes between 0.9.3 and 0.9.5
Fixes
Fix python -m pdb … (#135)
Fix “TypeError: write() argument must be str, not bytes” with non-utf8 terminal encodings (#63)
Handle pdb.Pdb._previous_sigint_handler with Python 3.5.3+ (#87)
Misc
Use shutil.get_terminal_size if available (#125)
Slightly improve loading of pdb from stdlib (#133)
NOTE: 0.9.4 was not released to PyPI due to Travis CI misconfiguration.
Changes between 0.9.2 and 0.9.3
Features
Improve sticky_by_default: don’t clear screen the first time (#83)
Handle header kwarg added with Python 3.7 in pdb.set_trace (#115)
config: allow to force use_terminal256formatter (#112)
Add heuristic for which ‘list’ is meant (#82)
Fixes
Skip/step over pdb.set_trace() (#119)
Handle completions from original pdb (#116)
Handle set_trace being invoked during completion (#89)
_pdbpp_path_hack/pdb.py: fix ResourceWarning (#97)
Fix “python -m pdb” (#108)
setup/interaction: use/handle return value from pdb.Pdb.setup (#107)
interaction: use _cmdloop if available (#106)
Fixed virtualenv sys.path shuffling issue (#85)
set_trace: do not delete pdb.curframe (#103)
forget: only call pdb.Pdb.forget with GLOBAL_PDB once
Tests
Travis: test pypy3
Travis/tox: add py37, remove nightly
tests: PdbTest: use nosigint=True (#117)
Add test_debug_with_overridden_continue (#113)
tests: re-enable/fix test_utf8 (#110)
tests: fix conditional skipping with test_pdbrc_continue
tests: runpdb: print output on Exceptions
pytest.ini: addopts: -ra
tests: handle pytest’s --assert=plain mode
tests: harden check: match all lines
tests: fix flake8 errors and invalid-escape DeprecationWarnings
Misc
setup.py: add trove classifier for “… :: Debuggers”
doc: separate usage section (#105)
Format code: flake8 clean, using autopep8 mainly (#118)
Add wheels support
README: grammar and example for break_on_setattr (#99)
README: fix formatting
Simplify the code, since we no longer support python 2.6
Changes between 0.9.1 and 0.9.2
Add LICENSE.txt.
Improved import time for __version__.
Changes between 0.8.2 and 0.9.1
Install ordereddict package only on Python versions older than 2.7.
Python 3 support
Improved Windows support
Changes between 0.8.1 and 0.8.2
- fix wheel packaging; see
https://bitbucket.org/antocuni/pdb/pull-request/38/support-pip-install-and-wheels-natively/diff
Changes between 0.8 and 0.8.1
- fix issue 37: Doesn’t install on OS X
Removed dependency on backports.inspect
Made dependency on funcsigs optional.
(https://bitbucket.org/antocuni/pdb/issue/37/doesn-t-install-on-os-x)
(https://bitbucket.org/antocuni/pdb/pull-request/35/use-funcsigs-package-instead-of/diff)
Changes between 0.7.2 and 0.8
Python 3 compatibility
Optional support for 256 color formatting by setting use_terminal256formatter = True in ~/.pdbrc.py (https://bitbucket.org/antocuni/pdb/pull-request/30)
Make set_trace() in same program remember previous state (https://bitbucket.org/antocuni/pdb/pull-request/33)
Append ? and ?? to callable to get info a la IPython (https://bitbucket.org/antocuni/pdb/pull-request/25)
Don’t treat lines like r = 5 or c = 6 as commands (https://bitbucket.org/antocuni/pdb/pull-request/11)
fix issue 20: support argument-free post mortem
Changes between 0.7.1 and 0.7.2
don’t crash with newer versions of py.test
Changes between 0.7 and 0.7.1
The pp (pretty print) command now uses the actual width of the terminal, instead of hardcoding 80 columns
py.test and unittest internal frames are hidden by default (type help hidden_frames for more info)
don’t crash if py is installed but py.test is not