Debug FastAPI with pdb


This is a method for debuggging FastAPI with pdb debugger.

pdb

pdb is a debugger built right into the python standard library. You don't need to install a library. You don't need a fancy IDE.

FastAPI

This debugging method should have broad application, but here we will use it in a FastAPI app.

FastAPI is a framework for building web APIs in python. Here's a basic application:

from datetime import datetime
from fastapi import FastAPI

app = FastAPI()

@app.get('/')
def root():
    now = datetime.now()
    return { "now": now }

There's one endpoint. It returns the current time.

Run with uvicorn

uvicorn is an ASGI web server implementation. You can use it to run FastAPI. Install:

poetry add uvicorn

And start:

uvicorn main:app --reload

The main:app argument is the path to your app variable inside the main.py module. The --reload flag detects changes to the files and restarts the server automatically for dev mode.

If we call it, it works:

❯ curl localhost:8000
{"now":"2022-06-02T06:53:58.376421"}%

pdb Debugging

This is all a contrived example. There's not too much to debug. But let's add a breakpoint and try it out. Let's determine what the value of now is at runtime.

The breakpoint is added by adding code. You need to import pdb and the set the breakpoint. Do that with a one-liner:

import pdb; pdb.set_trace()

Add that line below the now variable assignment:

@app.get('/')
def root():
    now = datetime.now()
    import pdb; pdb.set_trace()
    return { "now": now }

And stop and restart uvicorn, now with a --debug flag:

uvicorn main:app --reload --debug

Then curl to hit the code:

❯ curl localhost:8000

It'll hang, and the process for uvicorn will change from the usually app log to stdout to show a pdb repl:

> /Users/jaketrent/dev/ohai-fastapi/main.py(10)root()
-> return { "now": now }
(Pdb)

It stops on the line of the breakpoint. Here, you can examine the local variables by name. We want to see the value of now, so we type:

(Pdb) now
datetime.datetime(2022, 6, 2, 8, 2, 50, 139827)

And look at that! We see the value at runtime.

At the (Pdb) prompt, you can run the usual debugger commands. These are done with text commands. Type ? to see a list of possible commands:

(Pdb) ?

Documented commands (type help ):
========================================
EOF    c          d        h         list      q        rv       undisplay
a      cl         debug    help      ll        quit     s        unt
alias  clear      disable  ignore    longlist  r        source   until
args   commands   display  interact  n         restart  step     up
b      condition  down     j         next      return   tbreak   w
break  cont       enable   jump      p         retval   u        whatis
bt     continue   exit     l         pp        run      unalias  where

Miscellaneous help topics:
==========================
exec  pdb

You can run help on any of these commands, such as help c, and you can see:

(Pdb) help c
c(ont(inue))
        Continue execution, only stop when a breakpoint is encountered.

Now that we're done debugging, we'll press c for continue to move past our breakpoint. At that point, curl returns:

{"now":"2022-06-02T08:02:50.139827"}

pdb is a capable debugger. It doesn't require extra tooling. FastAPI can't keep any secrets from us!