Debugging Jupyter notebooks

5 minute read

While searching for ways to debug code in a Jupyter notebook, I found a lot of outdated posts. So I decided to quickly write up my findings.

(Just show me the answer…)

Let’s say we have this piece of code and we want to set a breakpoint between the original answer to the ultimate question of life, the universe and everything stored in answer and the addition to that answer:

def add_to_life_universe_everything(x):
    answer = 42
    # we want a breakpoint here
    answer += x
    
    return answer

add_to_life_universe_everything(12)

pdb

The built-in Python debugger pdb works just fine in a Jupyter notebook. With import pdb; pdb.set_trace() we can enter the debugger and get a little interactive prompt.

def add_to_life_universe_everything(x):
    answer = 42
    import pdb; pdb.set_trace()
    answer += x
    
    return answer

add_to_life_universe_everything(12)

In the debugger we can then print our variables, evaluate code to inspect the current stack, etc.

> <ipython-input>(4)add_to_life_universe_everything()
-> answer += x
(Pdb) answer
42
(Pdb) n
> <ipython-input>(6)add_to_life_universe_everything()
-> return answer
(Pdb) answer
54
(Pdb) c

Here, we print answer, then execute and go to the next line with n, print answer again and then continue the program with c. You can get an overview of the commands with h, or specifically with help <command>.

ipdb

There’s also ipdb but importing and calling it directly only works in the terminal, not in notebooks (see this issue).

IPython.core.debugger.Tracer

Using the Tracer class was actually the first thing I found when looking for information regarding debugging Jupyter notebooks.

def add_to_life_universe_everything(x):
    answer = 42
    from IPython.core.debugger import Tracer; Tracer()() 
    answer += x
    
    return answer

add_to_life_universe_everything(12)

This brings up the debugging shell, but prompts a deprecation warning:

/usr/local/lib/python3.5/site-packages/ipykernel_launcher.py:3:
DeprecationWarning: `Tracer` is deprecated since version 5.1, directly use \`IPython.core.debugger.Pdb.set_trace()`.

Turns out we need the set_trace function from IPython.core.debugger.

IPython.core.debugger.set_trace

from IPython.core.debugger import set_trace

def add_to_life_universe_everything(x):
    answer = 42
    set_trace()
    answer += x
    
    return answer

add_to_life_universe_everything(12)

This works fine and brings us a little bit more comfort (e.g. syntax highlighting) than just using the built-in pdb.

Like to comment? Feel free to send me an email or reach out on Twitter.

Did this or another article help you? If you like and can afford it, you can buy me a coffee (3 EUR) ☕️ to support me in writing more posts. In case you would like to contribute more or I helped you directly via email or coding/troubleshooting session, you can opt to give a higher amount through the following links or adjust the quantity: 50 EUR, 100 EUR, 500 EUR. All links redirect to Stripe.