IPython Interactive Computing and Visualization Cookbook(Second Edition)
上QQ阅读APP看书,第一时间看更新

Debugging code with IPython

Debugging is an integral part of software development and interactive computing. A widespread debugging technique consists of placing the print() functions in various places in the code. Who hasn't done this? It is probably the simplest solution, but it is certainly not the most efficient (it is the poor man's debugger).

IPython is perfectly adapted for debugging, and the integrated debugger is quite easy to use (actually, IPython merely offers a nice interface to the native Python debugger pdb). In particular, tab completion works in the IPython debugger. This recipe describes how to debug code with IPython.

How to do it...

There are two not-mutually exclusive ways of debugging code in Python. In the post-mortem mode, the debugger steps into the code as soon as an exception is raised, so that we can investigate what caused it. In the step-by-step mode, we can stop the interpreter at a breakpoint and resume its execution step by step. This process allows us to check carefully the state of our variables as our code is executed.

Both methods can actually be used simultaneously; we can do step-by-step debugging in the post-mortem mode.

The post-mortem mode

When an exception is raised within IPython, execute the %debug magic command to launch the debugger and step into the code. Also, the %pdb on command tells IPython to launch the debugger automatically as soon as an exception is raised.

Once you are in the debugger, you have access to several special commands, the most important ones being listed here:

  • p varname prints the value of a variable
  • w shows your current location within the stack
  • u goes up in the stack
  • d goes down in the stack
  • l shows the lines of code around your current location
  • a shows the arguments of the current function

The call stack contains the list of all active functions at a given location in the code's execution. You can easily navigate up and down the stack to inspect the values of the function arguments. Although quite simple to use, this mode should let you resolve most of your bugs. For more complex problems, you may need to do step-by-step debugging.

Step-by-step debugging

You have several options to start the step-by-step debugging mode. First, in order to put a breakpoint somewhere in your code, insert the following command:

import pdb
pdb.set_trace()

Second, you can run a script from IPython with the following command:

%run -d -b extscript.py:20 script

This command runs the script.py file under the control of the debugger with a breakpoint on line 20 in extscript.py (which is imported by script.py). Finally, you can do step-by-step debugging as soon as you are in the debugger.

Step-by-step debugging consists of precisely controlling the course of the interpreter. Starting from the beginning of a script or from a breakpoint, you can resume the execution of the interpreter with the following commands:

  • s executes the current line and stops as soon as possible afterwards (step-by-step debugging—that is, the most fine-grained execution pattern)
  • n continues the execution until the next line in the current function is reached
  • r continues the execution until the current function returns
  • c continues the execution until the next breakpoint is reached
  • j 30 brings you to line 30 in the current file

You can add breakpoints dynamically from within the debugger using the b command or with tbreak (temporary breakpoint). You can also clear all or some of the breakpoints, enable or disable them, and so on. You can find the full details of the debugger at https://docs.python.org/3/library/pdb.html.

There's more...

To debug your code with IPython, you typically need to execute it first with IPython—for example, with %run. However, you may not always have an easy way of doing this. For instance, your program may run with a custom command-line Python script, it may be launched by a complex bash script, or it may be integrated within a GUI. In these cases, you can embed an IPython interpreter at any point in your code (launched by Python), instead of running your whole program with IPython (which may be overkill if you just need to debug a small portion of your code).

To embed IPython within your program, simply insert the following commands somewhere in your code:

from IPython import embed
embed()

When your Python program reaches this code, it will pause and launch an interactive IPython terminal at this specific point. You will then be able to inspect all local variables, run any code you want, and possibly debug your code before resuming normal execution.

Most Python IDEs offer graphical debugging features (see the Efficient interactive computing workflows with IPython recipe). A GUI can sometimes be more convenient than a command-line debugger. A list of Python debuggers is available at https://wiki.python.org/moin/PythonDebuggingTools.