xdbg - Why xdbg?

Home

Why use xdbg?

First, it fixes some of the limitations of standard debuggers like pdb or ipdb. These debuggers are very good at helping you find bugs in your program. Once the problem is found, however, they provide essentially no ability to hotfix the problem -- you essentially have to let your program crash, and then run it again with fixed code.

This behavior works well with a code-compile-test workflow, but doesn't integrate well into a live-coding paradigm like Jupyter notebooks.

pdb doesn't let you modify return values

Consider the situation where you're not quite sure what code is needed for a function to have expected behavior. One thing you may want to do is drop yourself into a REPL inside that function, and then figure out what code needs to be there. After you're done, you'd want to exit the REPL while specifying a desired return value for the function.

Now let's try using pdb to modify the return value of a function (hint: you usually can't)!

In [1]:
def greeting():
    # We want to hotfix this function to return the right value
    return "ello, world!"

So let's load up the debugger:

In [3]:
%%debug
hello = greeting()
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.
> <string>(2)<module>()

ipdb> step
--Call--
> <ipython-input-1-9ca04e0c4013>(1)greeting()
----> 1 def greeting():
      2     # We want to hotfix this function to return the right value
      3     return "ello, world!"

ipdb> return "Hello, world!"
--Return--
'ello, world!'
> <ipython-input-1-9ca04e0c4013>(3)greeting()
      1 def greeting():
      2     # We want to hotfix this function to return the right value
----> 3     return "ello, world!"

ipdb> continue
In [4]:
hello
Out[4]:
'ello, world!'

The debugger command return didn't do what we want: you can't specify the return value! If you look at the documentation, you'll find that the return command doesn't even accept any arguments, and only allows you to run the return statement that's already part of the function.

pdb has limited ability to hotfix local variables

Let's consider another example where we might need some hotfixing: there's a typo in this function, which seems like something that should be fixable with a debugger. So let's try it!

In [5]:
def hello_world():
    messag = "Hello, world!"
    print(message)
In [6]:
%debug hello_world()
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.
> <string>(1)<module>()

ipdb> step
--Call--
> <ipython-input-5-4c5d8b7b2453>(1)hello_world()
----> 1 def hello_world():
      2     messag = "Hello, world!"
      3     print(message)

ipdb> next
> <ipython-input-5-4c5d8b7b2453>(2)hello_world()
      1 def hello_world():
----> 2     messag = "Hello, world!"
      3     print(message)

ipdb> next
> <ipython-input-5-4c5d8b7b2453>(3)hello_world()
      1 def hello_world():
      2     messag = "Hello, world!"
----> 3     print(message)

ipdb> message = messag
ipdb> continue
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-5-4c5d8b7b2453> in hello_world()
      1 def hello_world():
      2     messag = "Hello, world!"
----> 3     print(message)

NameError: name 'message' is not defined

The name message is not defined -- even though we tried to set the variable in the debugger!

pdb integration with Jupyter/IPython is not ideal

Note how calling pdb in this notebook environment calls up a UI that is basically an embedded CLI terminal. You get very few of the nice features of the notebook: you can't easily run multiple lines of code at the same time, use syntax highlighting, etc.

In short, the debugger is not a first-class citizen of the notebook ecosystem. It's very much designed for a code-compile-test workflow, not an interactive one. xdbg is one attempt to imagine what a first-class debugger for interactive Python would look like.