VSCode(Cursor)とFastAPIでデバッガを使う
概要
FastAPIのVSCodeでの開発時にデバッガを使う方法をまとめます。
PyCharmなど他のエディタでもデバッガは使用できますが、本記事では割愛し、VSCodeとCursorでのデバッガ方法をまとめます。
GitHub: https://github.com/hosimesi/code-for-techblogs/tree/main/fastapi_docker_debugger
デバッガの種類
Pythonでデバッガを使用したい場合、以下の2つが候補に上がってくると思います。
標準デバッガ (pdb)
Pythonに組み込まれている基本的なデバッガであり、コマンドラインで使用する方法とコード上に埋め込んで使用する方法の2種類があります。コマンドラインの場合、以下のようにモジュールを指定し、pdbに用意されているコマンドを使用して1行ずつ確認することができます。比較的小さなアプリケーションに向いているかと思います。
$ uv run python -m pdb pdb_cli_sample.py
$ uv run python -m pdb pdb_cli_sample.py
> fastapi_docker_debugger/pdb_cli_sample.py(1)<module>()
-> def main() -> int:
(Pdb) n
> fastapi_docker_debugger/pdb_cli_sample.py(8)<module>()
-> if __name__ == "__main__":
(Pdb) n
> fastapi_docker_debugger/pdb_cli_sample.py(9)<module>()
-> result = main()
(Pdb) s
--Call--
> fastapi_docker_debugger/pdb_cli_sample.py(1)main()
-> def main() -> int:
(Pdb) s
> fastapi_docker_debugger/pdb_cli_sample.py(2)main()
-> a = 1
(Pdb) p a
*** NameError: name 'a' is not defined
(Pdb) n
> fastapi_docker_debugger/pdb_cli_sample.py(3)main()
-> b = 2
(Pdb) p a
コードに埋め込む方法はコードに直接pdbをimportし、pdb.set_trace()をブレークポイントとして追加します。
import pdb
def main() -> int:
a = 1
b = 2
pdb.set_trace()
c = a + b
return c
if __name__ == "__main__":
result = main()
実行すると、先ほどと同じCLIの入力待ちになるので、あとはpdbのコマンドに従ってデバッグしていきます。pdb.set_trace()はPythonの組み込み関数であるbreakpoint()でも代替可能です。
Editorのデバッガ
IDEではGUIを利用したデバッグが可能で、ブレークポイントの設定や変数の監視が容易になります。VSCodeやCursorではlaunch.jsonというファイルにデバッガの設定を書いて起動します。内部的にはdebugpyが使われています。
debugpyは、Microsoftが開発しているPython用のDebug Adapter Protocolです。IDEをクライアントとして、debugpyがPythonプログラム内で動作し、デバッグ用のサーバーとして動作します。
これにより、ローカル環境のみならずリモート環境でも同様にデバッグを行えるようになります。
VSCodeでデバッグをしたい場合、該当ファイルを開き、ブレークポイントをつけてPython Debugger: Debug Python Fileを押すとデバッグが始まります。
そして変数名にカーソルを当てると変数の値が確認できます。こうすることで一発で全ての変数の値をGUIで確認することができます。
FastAPIと通常のスクリプトとの違い
FastAPIでも通常のスクリプトと同様に、pdb
やVSCodeのデバッガを使うことができますが、そのままでは使いづらい場合があります。チーム開発では、Docker Composeを使ってDocker環境を利用したり、Dev Containerを使用したり、ローカル環境で直接サーバーを起動したりすることが多いです。
また、通常のPythonスクリプトと異なり、Webフレームワークを使用したアプリケーションはサーバーとして起動し、HTTPリクエストを受け取って処理します。このサーバーは、uvicorn
やgunicorn
などのアプリケーションサーバーを介して動作します。そのため、デバッガを直接アタッチするには、いくつかの工夫が必要になります。
事前準備
まずは簡単なFastAPIのアプリケーションを立てます。パッケージマネージャーとしてはuvを使用しています。
- 環境の構築
$ uv init
$ uv add fastapi
- アプリケーションの作成
好きなeditorでFastAPIのコードを作成します。
$ editor main.py
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def health():
a = 1
b = 2
return {"Hello": "World", "result": a + b}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
FastAPIでデバッガを使う
FastAPIアプリケーションでデバッガを使用する方法を、以下の3つのケースに分けて解説します。
- 直接使用する
- Docker Composeで使用する
- DevContainerで使用する
どの場合でもlaunch.jsonが必要になります。自身が使いたいものに合わせてlaunch.jsonをカスタマイズしたりパスを変更してください。
直接使用する
ローカルのPython環境を直接使用するには、Pythonのパスをuvによって作られた環境を通す必要があるので、以下のようにdefaultInterpreterPathにパスを通します。この辺りのパスは自身の環境に合わせて修正してください。
# launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: FastAPI with uv",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"main:app",
"--host",
"0.0.0.0",
"--port",
"8000",
"--reload"
],
"cwd": "${workspaceFolder}/fastapi_docker_debugger",
"jinja": true
}
]
}
# settings.json
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
}
そして、Python Debugger: Debug using launch.jsonで実行します。
あとは同じようにブレークポイントをつけると、リクエストがそこまで到達した時にデバッグが可能になります。
Docker Composeで使用する
Dockerで使用するには、Docker環境内で動いているサーバからデバッグサーバにリクエストが通るようにする必要があります。debugpyを使うためuv経由でinstallしておきます。
$ uv install debugpy
そして、uvのFastAPIプロジェクトに従って、Dockerfileを作成します。
FROM python:3.13-slim
COPY /uv /uvx /bin/
COPY . /app
WORKDIR /app
RUN uv sync --frozen --no-cache
そして、compose.yamlも作成します。
services:
fastapi-docker-debugger:
build:
context: .
dockerfile: docker/Dockerfile
command: ["/app/.venv/bin/python", "-m", "debugpy", "--listen", "0.0.0.0:5678", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
container_name: fastapi-docker-debugger
ports:
- "8000:8000"
- "5678:5678"
expose:
- "5678"
volumes:
- .:/app
environment:
- PYTHONUNBUFFERED=1
- PYTHONDONTWRITEBYTECODE=1
ここで、debugpyを5678ポートに指定します。次にlaunch.jsonを準備します。
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: FastAPI in Docker",
"type": "debugpy",
"request": "attach",
"port": 5678,
"host": "localhost",
"pathMappings": [
{
"localRoot": "${workspaceFolder}/fastapi_docker_debugger",
"remoteRoot": "/app"
}
]
}
]
}
ここではattatch形式でdebugpyを起動させ、リモートの/appと5678番ポートで通信するように設定します。これらを準備できたらコンテナを起動します。
$ docker compose up --build
そして先ほどまでと同様にPython Debugger: Debug using launch.jsonでデバッガを起動し、ブレークポイントを仕込んでcurl http://localhost:8000をすると以下のようにデバッグすることができます。
DevContainerで使用する
DevContainerを使用する場合もDocker環境を使うので、上記とほぼ同じですが、devcontainer.jsonのみ準備します。ここでは、compose.yamlとservice名を指定しています。
{
"name": "FastAPI Dev Container",
"dockerComposeFile": "../compose.yaml",
"service": "fastapi-docker-debugger",
"workspaceFolder": "/app"
}
そして、Reopen in Containerでコンテナ環境でフォルダを開きます。するとすでにFastAPIサーバが立っているので、これまでと同様にデバッガを起動します。そして先ほどと同様にブレークポイントを仕込んでcurl http://localhost:8000をすると以下のようにデバッグすることができます。
終わりに
今回はローカルのDocker Container内のFastAPIでデバッガを使う方法をまとめました。
printデバッグを卒業し、デバッガを使うことで効率的な開発ができると思います。
参考
Discussion