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 |
---|---|
|
Show the current location in the file |
|
Display a list of commands or find help on a specific command |
|
Terminates the debugger and the program |
|
Exit the debugger, continue in the program |
|
Go to the next step in the program |
|
Repeat the previous command |
|
Print variables |
|
Step into a subroutine |
|
Return from a subroutine |
Further information on the IPython debugger can be found at ipdb.