🐛

VSCode + debugpy でPython CLIをターミナルから快適にデバッグする

2022/02/25に公開

先日より、大学のプロジェクトでPythonで書かれたCLIの開発に携わっています。そのデバッグにおいて、VSCodedebugpyの連携が便利だったので、設定方法と使い方をメモしておきます。

(デバッガに詳しくないだけの可能性があります。もっといいやり方があったら教えて下さい。)

CLIのデバッグ

主観ですが、VSCodeを含むIDE的な開発環境のデバッガと、CLI開発は相性が必ずしもよくないと思っています。そのような開発環境では、デバッグに使用する設定(インタプリタのパス、デバッグするファイル、引数など)を、VSCodeではConfigurationと呼ばれる単位で保存し、デバッグすることができます。

特に入力を持たないプログラムはもちろん、Web APIなどプログラムの起動後に外部から入力を与えるプログラムは、デバッガ経由で起動した後に任意の入力を与えることができるため、スムーズにデバッグをすることができます。

一方で、CLIはプログラムを呼び出すと同時に引数やフラグを指定します。そのため、私の知る限り、入力を変える場合にはConfigurationを変更する必要があり、手間に感じていました。

理想の世界では、ユニットテストが整備されており、テストをデバッグすればよいのですが、現実にはそうもいかない場合もあります。

そんなときに、debugpyを用いてプログラムをターミナルから起動しつつ、VSCodeでプログラムをデバッグすると便利です。

debugpy と VSCode の連携

debugpy は、Microsoftが開発しているDebug Adapter ProtocolのPython実装です。Debug Adapter Protocolを通して、サポートするクライアント(今回はVSCode)からPythonプログラムをデバッグすることができます。

詳細は、VSCodeのドキュメント "Python debugging in VS Code" ページの Command line debugging セクション に記載されていますが、ここには要点をまとめます。

VSCode側の設定

前提として、VSCodeのPython拡張のインストールが必要です。

https://aka.ms/pvsc-marketplace

その上で、VSCodeのワークスペース配下に .vscode/launch.json を作成し、以下の内容を記述します。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: Remote Attach",
      "type": "python",
      "request": "attach",
      "connect": {
        "host": "localhost",
        "port": 5678
      },
      "pathMappings": [
        {
          "localRoot": "${workspaceFolder}",
          "remoteRoot": "."
        }
      ]
    }
  ]
}

connect の部分で指定されたホスト、ポートで動作するDebug Adapter Protocol Serverに接続を試みます。

launch.json は、左サイドバーの Run and Debug ペーンから "create a launch.json file" をクリックし、言語にPython、Configurationに "Remote Attach" を選択することでも作成できます。

create a launch.json file

configuration選択

指定したポートは、debugpyを使用する際のポート番号と合致する必要があります。

debugpyのインストール・設定

次に、debugpyをインストールし、そちらを利用してプログラムを起動するようにします。

まず、pipを使ってdebugpyをインストールします。

https://pypi.org/project/debugpy/

pip install debugpy

あとは、Pythonプログラムを起動する際に、

python -m debugpy --wait-for-client --listen 5678 foo.py arg1 arg2

の形式で起動するようにします。

  • -m debugpy によって、debugpyがロードされます
  • --wait-for-client によって、クライアント(VSCode)が接続されるまで実行を遅延します
  • --listen によって、使用するポートを指定します。VSCodeのConfigurationと同じものを指定する必要があります。
  • ファイル名より後は、CLIを起動する際と同じ指定をします。
    • 私の場合は __main__.py を指定しています。

--wait-for-client によって、プログラムは接続待ちの状態になります。ここで、VSCodeの Run and Debug ペーンから先程作成したConfigurationを実行すると、debugpyがVSCodeに接続し、実行が始まります。この際、VSCodeでブレークポイントを貼っておくと、デバッガが表示され、変数の中身の確認などができます。

スクリプトを使って起動を簡単に

ここまでで、debugpyとVSCodeを連携したデバッグができました。しかし、毎回 python -m debugpy --wait-for-client --listen 5678 と打ち込むのは面倒ですね。

開発用に、デバッガ付きで引数をプログラムに渡してくれるシェルスクリプトを作成すると、便利でした。

#!/bin/bash
python -m debugpy --wait-for-client --listen 5678 ./src/__main__.py ${@:2}

これを cli-dev のようなファイル名でワークスペース直下に置いて、実行権限をつけておくと ./cli-dev arg1 arg2 のように普通にCLIを呼び出すようにプログラムを起動し、デバッグもすることができます。

実際に動作させている様子です。Integrated Terminalからスクリプト経由で様々なフラグを渡し、起動したプログラムをVSCodeでデバッグできています。

まとめ

VSCodeのドキュメントに書いてある内容ではありますが、日本語の情報も多くなさそうだったのでメモも兼ねてまとめました。

デバッガを使うとデバッグの速度は向上しますが、特にCLI開発の場合は設定周りが面倒で、ついprintデバッグに走ってしまっていました。VSCodeとdebugpyを連携し、必要なコマンドをスクリプトにまとめておくことで、通常のコマンド実行と同じようにデバッガを使うことができます。

CLIデバッグにより良い方法があればぜひ教えて下さい🙇‍♂️

Discussion