Debugging#

IPython contains various tools to analyse faulty code, essentially the exception reporting and the debugger.

Check exceptions with %xmode#

If the execution of a Python script fails, an exception is usually thrown and relevant information about the cause of the error is written to a traceback. With the %xmode magic function you can control the amount of information that is displayed in IPython. Let’s look at the following code for this:

[1]:
def func1(a, b):
    return a / b


def func2(x):
    a = x
    b = x - 1
    return func1(a, b)
[2]:
func2(1)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[2], line 1
----> 1 func2(1)

Cell In[1], line 8, in func2(x)
      6 a = x
      7 b = x - 1
----> 8 return func1(a, b)

Cell In[1], line 2, in func1(a, b)
      1 def func1(a, b):
----> 2     return a / b

ZeroDivisionError: division by zero

Calling func2 leads to an error and the traceback shows exactly what happened: each line shows the context of each step that ultimately led to the error. With the %xmode magic function (short for exception mode) we can control which information should be displayed to us.

%xmode takes a single argument, the mode, and there are three options: * Plain * Context * Verbose

The default setting is Context and outputs something like the one above. Plain is more compact and provides less information:

[3]:
%xmode Plain
func2(1)
Exception reporting mode: Plain
Traceback (most recent call last):

  Cell In[3], line 2
    func2(1)

  Cell In[1], line 8 in func2
    return func1(a, b)

  Cell In[1], line 2 in func1
    return a / b

ZeroDivisionError: division by zero

The Verbose mode shows some additional information, including the arguments for any functions being called:

[4]:
%xmode Verbose
func2(1)
Exception reporting mode: Verbose
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[4], line 2
      1 get_ipython().run_line_magic('xmode', 'Verbose')
----> 2 func2(1)

Cell In[1], line 8, in func2(x=1)
      6 a = x
      7 b = x - 1
----> 8 return func1(a, b)
        a = 1
        b = 0

Cell In[1], line 2, in func1(a=1, b=0)
      1 def func1(a, b):
----> 2     return a / b
        a = 1
        b = 0

ZeroDivisionError: division by zero

This additional information can help narrow down the reason for the exception. Conversely, however, the Verbose mode can lead to extremely long tracebacks in the case of complex code, in which the essential points can hardly be recognized.

Debugging with %debug#

Debugging can help if an error cannot be found by reading a traceback. The Python standard for interactive debugging is the Python debugger pdb. You can use it to navigate your way through the code line by line to see what is possibly causing an error. The extended version for IPython is ipdb.

In IPython, the %debug-magic command is perhaps the most convenient way to debug. If you call it after an exception has been thrown, an interactive debug prompt will automatically open during the exception. Using the ipdb prompt, you can examine the current status of the stack, examine the available variables and even run Python commands.

Let’s look at the last exception, then do some basic tasks:

[5]:
%debug
> /var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_21353/3792871231.py(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 
      5 def func2(x):

ipdb> print(a)
1
ipdb> print(b)
0
ipdb> quit

However, the interactive debugger does a lot more – we can also go up and down the stack and examine the values of variables:

[6]:
%debug
> /var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_21414/3792871231.py(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 
      5 def func2(x):

ipdb> u
> /var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_21414/3792871231.py(8)func2()
      4 
      5 def func2(x):
      6     a = x
      7     b = x - 1
----> 8     return func1(a, b)

ipdb> u
> /var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_21414/1541833627.py(2)<module>()
      1 get_ipython().run_line_magic('xmode', 'Verbose')
----> 2 func2(1)

ipdb> d
> /var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_21414/3792871231.py(8)func2()
      4 
      5 def func2(x):
      6     a = x
      7     b = x - 1
----> 8     return func1(a, b)

ipdb> print(x)
1
ipdb> list
      3 
      4 
      5 def func2(x):
      6     a = x
      7     b = x - 1
----> 8     return func1(a, b)

ipdb> q

This greatly simplifies the search for the function calls that led to the error.

If you want the debugger to start automatically when an exception is thrown, you can use the %pdb-magic function to enable this behavior:

[7]:
%xmode Plain
%pdb on
func2(1)
Exception reporting mode: Plain
Automatic pdb calling has been turned ON
Traceback (most recent call last):

  Cell In[7], line 3
    func2(1)

  Cell In[1], line 8 in func2
    return func1(a, b)

  Cell In[1], line 2 in func1
    return a / b

ZeroDivisionError: division by zero

> /var/folders/hk/s8m0bblj0g10hw885gld52mc0000gn/T/ipykernel_21437/3792871231.py(2)func1()
      1 def func1(a, b):
----> 2     return a / b
      3 
      4 
      5 def func2(x):

ipdb> p(b)
0
ipdb> q

If you have a script that you want to run in interactive mode from the start, you can do so with the command %run -d.

Essential commands of the ipdb#

Command

Description

list

Show the current location in the file

h(elp)

Display a list of commands or find help on a specific command

q(uit)

Terminates the debugger and the program

c(ontinue)

Exit the debugger, continue in the program

n(ext)

Go to the next step in the program

<enter>

Repeat the previous command

p(rint)

Print variables

s(tep)

Step into a subroutine

r(eturn)

Return from a subroutine

Further information on the IPython debugger can be found at ipdb.