Pythonのデバッグを完全理解

# デバッグ

　デバッグは開発者にとって、とても重要なスキルの1つです。デバッグをすることで、エラーを正確に特定し、プログラムのバグを見つけることができます。Pythonでは、さまざまなデバッグツールやパッケージ（デバッガーとも言う）が提供されています。これらをどう使うかを紹介していきます。

## pdbでデバッグ

　[「pdb」](https://docs.python.org/ja/3/library/pdb.html)はPython標準ライブラーのデバッグツールで、Pythonプログラムにインタラクティブなソースコードデバッグ機能を提供しています。使い方はC言語の[「gdb」](https://ja.wikipedia.org/wiki/GNU%E3%83%87%E3%83%90%E3%83%83%E3%82%AC)と類似しています。`pdb`の主な機能として、[「ブレークポイント」](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AC%E3%83%BC%E3%82%AF%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88)の設置、[「ステップ実行」](https://wa3.i-3-i.info/word15249.html)、「[スタックフレーム](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%BC%E3%83%AB%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF)」のチェック、変数の値を動的に変更するなどあります。`pdb`はよく使われるデバッグ操作コマンドを提供しています。

|コマンド  |短縮コマンド  |説明  |
|---|---|---|
|break  |b  |ブレークポイントの設置；`b <行数>`で指定行数にブレークポイントを設置できる  |
|continue  |cont/c  |次のブレークポイントまで実行再開  |
|next  |n  |次の行を実行する；次の行はサブプログラム（関数など）の場合、中に入らない  |
|step  |s  |次の行を実行する；次の行はサブプログラム（関数など）の場合、中に入る  |
|where  |bt/w  |[「スタックトレース」](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B9)を表示　 |
|enable  |-  |無効にされたブレークポイントを有効にする  |
|disable  |-  |有効にされたブレークポイントを無効にする  |
|pp/p  |-  |`p <変数名>`で変数をプリントする  |
|list  |l  |現在行周辺のソースを表示（llコマンドで、より広範囲のソースを表示できる）  |
|up  |u  |上のスタックフレームへ移動  |
|down  |d  |下のスタックフレームへ移動  |
|restart  |run  |デバッグをリスタートする  |
|args  |a  |関数の引数をプリントする  |
|clear  |cl  |全てのブレークポイントを削除；`cl <番号>`で指定番号のブレークポイントを削除できる  |
|return  |r  |関数の終わりのまで実行する  |
|help  |h  |`h <コマンド名>`でコマンドのhelpを表示する  |
|quit  |q  |デバッグを終了する  |

　2通りの方法で、`pdb`デバッガーを起動できます。1つは、コマンドライン引数で`pdb`モジュールを指定し、Pythonファイルを起動する方法です。

```bash
python -m pdb test_pdp.py
```

　もう1つは、Pythonコードの中で、`pdb`モジュールの`set_trace`でブレイクポイントを設定する方法です。プログラムは、ブレイクポイントまで実行されたら、自動的に中断し、pdbデバッガーを起動します。

```python
import pdb


def factorial(n, sum=0):
    if n == 0:
        return sum

    pdb.set_trace()
    sum += n
    print(sum)
    return factorial(n-1, sum)


if __name__ == '__main__':
    factorial(5)
```

　この2つの方法の間に特に違いはないが、どれを選ぶかはケースバイケースです。コードが短い場合は、コマンドライン引数方式で`pdb`を起動しても問題ないでしょう。逆に、大きなプログラムになると、デバッグしたい箇所に事前に`set_trace`でブレイクポイントを設置したほうがやりやすいです。

　`pdb`デバッガーが起動されたら、前述のコマンドでデバッグすることができるようになります。下の例は、末尾再帰の階乗関数をデバッグしたものです。まず、`bt`で該当関数の呼び出しスタックをチェックし、`list`でPythonのコードを見ました。次に、`p`で`sum`の値をプリントし、`r`で再帰の`return`を追ってみました。そして最後に、`q`でデバッグを終了しました。

```bash
kaito@MacBook-Pro debug % python factorial.py 
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
-> sum += n
(Pdb) bt
  /Users/kaito/Desktop/debug/factorial.py(15)<module>()
-> factorial(5)
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
-> sum += n
(Pdb) list
  4     def factorial(n, sum=0):
  5         if n == 0:
  6             return sum
  7  
  8         pdb.set_trace()
  9  ->     sum += n
 10         print(sum)
 11         return factorial(n-1, sum)
 12  
 13  
 14     if __name__ == '__main__':
-> sum += n
(Pdb) p sum
0
-> sum += n
(Pdb) r
5
> /Users/kaito/Desktop/fluent python/debug/factorial.py(9)factorial()
-> sum += n
(Pdb) r
9
> /Users/kaito/Desktop/fluent python/debug/factorial.py(9)factorial()
-> sum += n
(Pdb) r
12
> /Users/kaito/Desktop/fluent python/debug/factorial.py(9)factorial()
-> sum += n
(Pdb) r
14
> /Users/kaito/Desktop/fluent python/debug/factorial.py(9)factorial()
-> sum += n
(Pdb) r
15
--Return--
> /Users/kaito/Desktop/debug/factorial.py(11)factorial()->15
-> return factorial(n-1, sum)
(Pdb) q
Traceback (most recent call last):
...
```

　`pdb`の出力は若干地味に感じるなら、出力を綺麗にしてくれる`ipdb`というパッケージもあります。REPL環境を綺麗にしてくれる、`ipython`と同じイメージです。APIは同じですので、`import pdb`を`import ipdb as pdb`にすればそのまま使えます。

```bash
kaito@MacBook-Pro debug % python factorial.py
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
      8     ipdb.set_trace()
----> 9     sum += n
     10     print(sum)

ipdb> r                                                                                   
5
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
      8     ipdb.set_trace()
----> 9     sum += n
     10     print(sum)

ipdb> r                                                                                   
9
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
      8     ipdb.set_trace()
----> 9     sum += n
     10     print(sum)

ipdb> r                                                                                   
12
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
      8     ipdb.set_trace()
----> 9     sum += n
     10     print(sum)

ipdb> r                                                                                   
14
> /Users/kaito/Desktop/debug/factorial.py(9)factorial()
      8     ipdb.set_trace()
----> 9     sum += n
     10     print(sum)

ipdb> r                                                                                   
15
--Return--
15
> /Users/kaito/Desktop/debug/factorial.py(11)factorial()
     10     print(sum)
---> 11     return factorial(n-1, sum)
     12 

ipdb> q                                                                                   
Exiting Debugger.
```

　本当は色鮮やかでもっと綺麗ですよ。

　`ipub`以外に、「[web-pdb](https://github.com/romanvm/python-web-pdb)」というブラウザーでデバッグできるパッケージもあります。そして、`pdb`とAPIも同じです。イメージとしては以下の図を見れば分かると思います。
![](https://raw.githubusercontent.com/romanvm/python-web-pdb/master/screenshot.png)

　また、vimが好きな人はCUIデバッガーの「[PuDB](https://github.com/inducer/pudb)」もチェックしてください。イメージとしては以下の図のようになります。CUI好きにはたまらないですね。また、使い方に関しては@Kernel_OGSunさんの[記事](https://qiita.com/Kernel_OGSun/items/144c8502ce2eaa5e4410)をお勧めします。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;![](https://camo.githubusercontent.com/2de435c5383feb76e3d4a97ee949bfcc8c0ea9e3/68747470733a2f2f74696b65722e6e65742f7075622f707564622d73637265656e73686f742e706e67)
## breakpointでデバッグ
　Python 3.7から`breakpoint`というビルドイン関数が追加されました（[PEP 553](https://www.python.org/dev/peps/pep-0553/)）。基本的な使い方は以下のようになります。

```python
def factorial(n, sum=0):
    if n == 0:
        return sum

    breakpoint()
    sum += n
    print(sum)
    return factorial(n-1, sum)


if __name__ == '__main__':
    factorial(5)
```

　デフォルトでは、`import pdb; pdb.set_trace()`と全く一緒です。

　高度な使い方として、`PYTHONBREAKPOINT`という環境変数を設定することで、色々な機能を追加できます。例えば、`PYTHONBREAKPOINT=0`で、デバッグをオフにすることができ、コードの中の`breakpoint()`を1個1個消す必要がなくなります。

　それから、`PYTHONBREAKPOINT='importable.callable'`を設定することで、`importable.callable`が`import`され、`callable`が呼び出されるようになります。例えば、次にように、`web-pdb`を使用できます。

```bash
PYTHONBREAKPOINT='web_pdb.set_trace' python test_pdb.py
```

　環境変数だけ変えれば、Pythonのコードを1行も変えずに、デバッガーを交換でき、とても便利ですね。

## Visual Studio Codeでデバッグ

　VSCodeでデバッグするのは非常に簡単です。

　まず、左のツールバーから虫アイコンをクリックします。初回の時に、`launch.json`を作成する必要がありますが、デフォルトのままでほぼ問題ありません。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img width=50 alt="Screen Shot 2020-09-30 at 23.25.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/7d262169-491a-142e-9401-74851f4eeabc.png">

　続いて、行番号の横をクリックすれば、ブレイクポイントを設置できます。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img width=300 alt="Screen Shot 2020-09-30 at 23.34.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/db709c1c-9ef7-c548-3a75-0982b2489562.png">

　次に、デバッグ開始をクリックすると、実行されます。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img width=300 alt="Screen Shot 2020-09-30 at 23.39.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/37cc549a-0eca-7d65-9ee2-c64970eeb631.png">

　そうすると、次の画面になります。
![Screen Shot 2020-09-30 at 23.42.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/71237b8b-a20e-bc56-8c60-87aa75a327ae.png)

　右上のボタンを利用して、デバッグを操作できます。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;![Screen Shot 2020-09-30 at 23.57.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/fe8b6ef3-2cfd-65e3-df43-05577d53ddec.png)

　`Step Over`は`pdb`の`next`に相当する機能です。また、`Step In`は`pdb`の`step`、`Step Out`は`return`になります。GUIは使いやすいですね。

## PyCharmでデバッグ
　基本、VSCodeと同じようなインタフェースですね。
![Screen Shot 2020-10-01 at 0.05.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/a7ed78f5-9836-d939-07a0-ae23ce5fe0e4.png)

　右上の虫アイコンでデバッグ開始し、正方形アイコンで終了します。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img width=300 alt="Screen Shot 2020-10-01 at 0.07.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/98ab94fa-d759-eb48-9dec-3425ea6a8f21.png">

　デバッグを操作したい時、主にこちらのボタンが使えます。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img width=300 alt="Screen Shot 2020-10-01 at 0.10.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/f27496d1-492a-62da-d356-e2bac55f622b.png">

　左の二子玉を押すと、下の画面に入ります。`Condition`でブレイクポイントに入る条件を設定できるのは非常に便利ですね。
![Screen Shot 2020-10-01 at 0.14.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/a81575ee-3533-45d6-27ab-d162dedd3c5b.png)

　一番右の電卓アイコンを押すと、下の画面に入ります。変数などの検証ができます。
![Screen Shot 2020-10-01 at 0.16.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246522/328befac-c47b-82bf-cb0f-5c5ae272d3fe.png)

# まとめ
　Pythonのデバッグについて色々見てきました。まずは、標準ライブラリーの`pdb`とその派生です。Pythonの開発者として、とりあえず`pdb`はおさえましょう。それから、VSCodeとPyCharmのGUIデバッガーも見てみました。基本的に非常に使いやすいものなので、`pdb`さえ分かれば、すぐ使えてしまうでしょう。もちろん、高度な使い方は実践で経験を積むしかないですね。