A couple conversations I had at DjangoCon last week reminded me that I’d never gotten around to finishing this little post.
…And yet, in Python/Django — the stack I spend most of my time working with — I’d stuck to the poor habit of throwing
request.POST for a Django view. And then I came across and started tinkering with
Here’s a brief overview on how to rock your Python debugging workflow by using an actual interactive debugger. The example below highlights the most basic of usecases, but it’s a good start.
Using pdb with Django
Say, you have a view that looks like this:
from django.http import HttpResponsedef default(request):# completely innocuous variablesfoo = 1bar = 0# completely innocuous divisionni = foo/barreturn HttpResponse("Foo says %d" % ni, mimetype="text/plain")
And say, you run your local development server with:
When accessed, this view will generally throw you something like this:
By replacing the
manage.py runserver command with one wrapped in PDB, we can start digging a little bit deeper into this problem view.
python -m pdb manage.py runserver
You will notice that the shell hangs on a
(Pdb) prompt. Enter
c for “continue” (command reference here) and hit enter. (You’ll also notice that control-c restarts
runserver, since the default PDB behavior is to restart a script after it ends. Just hit control-c a few times to break PDB’s execution loop when you want to kill the server.)
You’ll notice that running under PDB doesn’t do anything by itself.
You’ll need to set a breakpoint to actually tell PDB where to stop execution. Inserting the following line of code causes PDB to break when it executes — which, in turn, triggers the debugging shell.
import pdb; pdb.set_trace()
In our example, we’d like to inspect where our exception is getting thrown, so we’ll throw it right before the offending line of code:
from django.http import HttpResponsedef default(request):# completely innocuous variablesfoo = 1bar = 0# well, this is where our error is, so let's trace itimport pdb; pdb.set_trace()ni = foo/barreturn HttpResponse("Foo says %d" % ni, mimetype="text/plain")
Congrats! Your browser is now waiting for your view to finish, your view is waiting (at the breakpoint) for PDB to finish, and PDB is waiting for you in the shell.
Doing things in PDB
At this point, the PDB shell acts like the normal
ipython shell. You can look at variables right in the scope of the breakpoint.
When working with a breakpoint, you can even screw around with local variables to try and find a fix, but note that only the last line you execute is remembered when you
continue. You can still execute several statements by using the semicolon:
Other neat things to try:
- If you’re working with utility functions that can be called from various places, you can use the
wherecommand get a full trace of how Python got to that point in your script. This is pretty much exactly as advertised.
- In addition, the Python
locals()functions (which spit out all global or local variables, respectively) can be used within this
(Pdb)prompt, which can be nifty if you’re debugging a GET/POST/COOKIES/SESSION issue.
I can’t say that I’ve been using PDB nearly as often as I’d like, but I have found it useful for the occasional mis-assigned variable or other super enigmatic bug.
Hope someone else finds it nearly as helpful as I have.