Improve your Django experience with IPython

2021-02-21 Make your shell better

IPython is an improved Python shell for interactive use. It has many great features such as syntax highlighting, autocomplete, and powerful “magic commands”. I love it, use it on every project, and use the IPython prompt for examples on my blog.

IPython has too much goodness to cover here. Check out its tutorial for an introduction.

To use IPython with Django, simply install it:

$ python -m pip install ipython

(Or add to your project’s requirements file.)

Now Django’s shell will automatically use IPython:

$ ./manage.py shell
Python 3.9.1 (default, Jan 21 2021, 09:04:53)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.20.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from db_buddy.core.models import Server|
                                                Server
                                                ServerKind
                                                ServerManager

In this example, I typed Server then pressed “Tab” and IPython opened a drop-down of possible names to import.

IPython’s debugger

IPython also comes with an improved version of Python’s pdb debugger, with colorization and other features. Inside the IPython shell, if you code raises an exception, you can open the IPython debugger with the %debug magic command:

In [1]: def divide(x, y):
   ...:     return x/y
   ...:

In [2]: divide(1,0)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-3-4fffc6193274> in <module>
----> 1 divide(1,0)

<ipython-input-2-97054f827698> in divide(x, y)
      1 def divide(x, y):
----> 2     return x/y
      3

ZeroDivisionError: division by zero

In [3]: %debug
> <ipython-input-2-97054f827698>(2)divide()
      1 def divide(x, y):
----> 2     return x/y
      3

ipdb> x
1
ipdb> y
0

In this session, I first added the divide() function and then ran it, and it crashed. At In [3] I typed %debug, which started the IPython debugger at the line of code that raised the ZeroDivisionError exception. This is the post_mortem() pdb method.

THe debugger showed the function with an arrow pointing at the line of code that raised the exception. I then checked the values of the x and y variables.

Using pdb, and by extension ipdb, is a skill in itself. For more information, check out:

Using IPython’s debugger outside of IPython

You can use IPython’s debugger outside of its shell by installing the wrapper package ipdb:

$ python -m pip install ipdb

Now you can start the debugger similar to pdb:

import ipdb; ipdb.set_trace()

But, since Python 3.7, the new breakpoint() built-in is the best way to start a debugger. This allows you to use an alternative debugger with the same syntax, maybe even with per-project customization.

By default breakpoint() uses pdb, but we can make it use ipdb by setting the PYTHONBREAKPOINT environment variable. You can do this at the top of your manage.py:

#!/usr/bin/env python
import os
import sys
from pathlib import Path

os.environ["PYTHONBREAKPOINT"] = "ipdb.set_trace"


def main():
    ...

Now if you need to start the debugger at some point in your code, you can add a call to breakpoint(). For example in a view:

def index(request):
    breakpoint()
    return render(request, "index.html")

Nice.

Using IPython’s debugger in tests

Django’s test framework has a --pdb flag to open a debugger on test failure. It will automatically use ipdb once you’ve installed it.

pytest has a similar --pdb flag. You can configure --pdb to use the IPython debugger by setting the --pdbcls flag. This is most conveniently done in the addopts setting in your pytest.ini (or other configuration file):

[pytest]
addopts = --pdbcls=IPython.terminal.debugger:TerminalPdb

Great!

Fin

May your adventures never cease,

—Adam


Want better tests? Check out my book Speed Up Your Django Tests which teaches you to write faster, more accurate tests.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: django