'How to change IPython %pdb and %debug debugger?
By default, ipython uses ipdb as debugger with %pdb or %debug magics. However, I much prefer pdb++... Is there a way of changing the debugger called with these magics ? (I am aware I can simply use pdb.xpm() on exception with pdb++, but I'd like to make it work with ipython magic commands so that I don't have to wrap the code each time...)
Solution 1:[1]
So at least for limited circumstances and not in a way I'd necessarily recommend, the answer here is yes. I can't promise the below will work outside the confines of what I did, but it might give you enough insight to play around with it yourself. Caution is warranted because it involves changing undocumented attributes of the ipython
shell class at runtime. TLDR: I hunted down how ipython
calls the debugger when the %pdb
magic is on or when you call the %debug
magic, and I updated it to use the debugger I wanted. Skip the next two paragraphs if you just want the approach that worked for me and don't care about the hunt.
Long version: when you run ipython
it starts an instance of TerminalInteractiveShell
, which has a debugger_cls
attribute telling you the debugger that ipython
will launch. Unfortunately, at the level of TerminalInteractiveShell
, debugger_cls
is actually a property of the class, and has no setter that lets you modify it. Rather, it either gets set to Pdb
(actually a more featureful ipython
Pdb
than the traditional pdb
) or TerminalPdb
(even more features).
If you dig deeper, however, you find that debugger_cls
gets passed up to InteractiveShell
to initialize how tracebacks are handled. There it seems to disappear into the initialization of InteractiveShell
's InteractiveTB
property, but actually just ends up as the debugger_cls
attribute of that (InteractiveTB
) class (by setting the inherited attribute from TBTools
). Finally, this debugger_cls
attribute only gets used to set the pdb
attribute (more or less by doing TBToolsInstance.pdb = TBToolsInstance.debugger_cls()
) in one of several places. In any case, it turns out that these attributes can be changed! And if you change them correctly they will percolate to the shell itself! Importantly, this relies on the fact that ipython
makes use of the Traitlets package to create a Singleton
object for the shell, and this allows you to gain access to that object from within the terminal itself. More on that below.
Below I show the code you can run in the ipython
shell to achieve your desired result. As an example, I'm replacing the default debugger (TerminalPdb
) with a modified version I created that deals more nicely with certain list comprehensions (LcTerminalPdb
). The process (which you can run in the ipython
shell) is as follows.
# import the TerminalInteractiveShell class
from IPython.terminal.interactiveshell import TerminalInteractiveShell
# grab the specific instance of the already initialized ipython
shl = TerminalInteractiveShell().instance()
# grab the InteractiveTB attribute (which is a class)
tbClass = shl.InteractiveTB
# load the debugger class you want to use; I assume it's accessible on your path
from LcTerminalPdb import LcTerminalPdb
# change tbClass's debugger_cls to the debugger you want (for consistency
# with the next line)
tbClass.debugger_cls = LcTerminalPdb
# more importantly, set the pdb attribute to an instance of the class
tbClass.pdb = tbClass.debugger_cls()
# The above line is necessary if you already have the terminal running
# (and have entered pdb at least once); otherwise, ipython will run it on
# its own
That's it! Note that because you call the instance()
method of TerminalInteractiveShell
, you are grabbing the object for the currently running shell, which is why the modifications will affect the shell itself and so all following debugs. For a bonus, you can add these lines of code to your ipython_config.py
file, so the debugger you want (LcTerminalPdb
here) is always loaded with ipython
:
c.InteractiveShellApp.exec_lines = [
'%pdb on',
'from LcTerminalPdb import LcTerminalPdb',
'from IPython.terminal.interactiveshell import TerminalInteractiveShell',
'shl = TerminalInteractiveShell().instance().InteractiveTB',
'shl.debugger_cls = LcTerminalPdb',
]
Note that above I don't need to write the extra shl.pdb = shl.debugger_cls()
line, as ipython
will take care of it the first time a debug point is entered. But feel free to, to be sure.
NOTES:
- I have only tested this using
LcTerminalPdb
, and only briefly, but it seems to work appropriately - I suspect as long as other
pdb
debuggers have the same API aspdb
(i.e. if they can be used by thePYTHONBREAKPOINT
environment variable) then it should work - It's really unclear to me whether changing such deep attributes will have unexpected effects, so not sure how much I recommend this approach
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | esg |