【Python】pdb/ipdb/pdb++でデバッグをより効率的に！

# はじめに
プログラムのデバッグによく使うのが`print()`があるが、`pdb`を使えばもっと効率よくできるそうなのでメモ

## pdbとは?
- pdbとはpython標準ライブラリの名称でpython用ソースコードデバッガです．
    - [公式ドキュメント: pdb --Pythonデバッガ](https://docs.python.org/ja/3/library/pdb.html)
    - 基本的にはpdbを使うことで十分デバッグ可能ですが，独自に拡張したい際には`Pdb`の継承クラスを定義することで拡張可能です．

- またpdbの拡張版として`ipdb`や`pdb++`があります
    - [GitHubリポジトリ: gotcha/ipdb](https://github.com/gotcha/ipdb)
    - [GitHubリポジトリ: pdbpp/pdbpp](https://github.com/pdbpp/pdbpp)

本記事では，はじめにpdbの使い方を説明した後，ipdbやpdb++についても簡単に説明します．

## pdbの使い方
はじめにpdbの使い方について説明していきます．

### 基本

以下のようなスクリプトを準備します．
- `mymodule.py`
    - `test()`: Hello Worldを５つprintする関数
```python:mymodule.py
def test():
    print("Hello World1")
    print("Hello World2")
    print("Hello World3")
    print("Hello World4")
    print("Hello World5")
```

- `main.py`
    - mymoduleをimportするスクリプト
```python:main.py
import gdb
import mymodule

pdb.run("mymodule.test()")
```
`main.py`内でデバッグしたいモジュールを`pdb.run()`に渡すことでデバッグが実行できます．

これで実行してみます．実行すると
```bash
$ python3 main.py
> <string>(1)<module>()
(Pdb)
```
となるので，`continue` または `c`と入力すればデバッグが実行されます．
今回はバグやブレイクポイントを置いて無いのでただprint()が行われるだけです．
```bash
$ python3 main.py 
> <string>(1)<module>()
(Pdb) continue  # または c
Hello World1
Hello World2
Hello World3
Hello World4
Hello World5
```

### ブレイクポイントを挿入

デバッグを行いたいポイントに`pdb.set_trace()`を書き込むことでブレイクポイントを挿入できます．

試しに先程の`mymodule.py`のprintの途中に入れてみます．

```python:mymodule.py
import pdb


def test():
    print("Hello World1")
    print("Hello World2")
    print("Hello World3")
    pdb.set_trace()
    print("Hello World4")
    print("Hello World5")

```
これで`main.py`を実行してみます．すると3つ目の`print("Hello World3)`の実行後，処理が一時停止しているのが分かります．
```bash
$ python3 main.py 
> <string>(1)<module>()
(Pdb) c
Hello World1
Hello World2
Hello World3
> /home/ktro2828/my_package/mymodule.py(9)test()
-> print("Hello World4")
(Pdb) c  # 一時停止後，再実行
Hello World4
Hello World5
```

一時停止後，以下のコマンドを使えば1行づつの実行なども可能です．

|コマンド       |表|
|---|---|
|s (step)      |現在の行を実行|
|n (next)      |現在の関数の次の行に達するか，関数が返るまで実行を継続|
|unt(il)       |行数を指定すると，指定行数かそれ以上に達するまで実行．引数なしだとstepと同じ|
|r (return)    |現在の関数が返るまで実行を継続|
|l (list)      |現在のファイルのソースコードを表示．引数を指定しないと，現在の行の前後11行分を表示|
|a (args)      |現在の関数の引数を表示|
|p             |現在のコンテキストにおいてexpressionを評価し，結果をプリント|
|c (cont(inue))|次のブレイクポイントまで実行|

### 挿入なしでデバッグ
- 上のようにいちいちブレイクポイントを挿入しなくてもデバッグ可能です．

```bash
$ python -m pdb <スクリプト名>
```
(@shiracamusさんありがとうございます。)

## ipdbについて

- [GitHubリポジトリ: gotcha/ipdb](https://github.com/gotcha/ipdb)

次にipdbについて説明します．といっても使い方自体はpdbとほとんど変わりません．

ipdbはpdbと違い，標準ライブラリではないので別途インストールが必要です．

```bash
$ pip install ipdb
```

- ipdbとpdbの違いとしては以下が挙げられます

### 1. デバッガのデザインに色がついている
ipdbではデバッガに色がつけられ，pdbに比べて見やすくなっています．

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624265/1dbefe31-2f40-12d7-cadf-b6da17e95d69.png width=480>

### 2. タブ補完が効く
ipdbではタブ補完によって候補を表示してくれます．

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624265/0441a872-5ae4-dabf-c047-aa072e081aa1.png width=480>

### 3. コマンドが増える
ipdbではpdbで使用可能なコマンドに加えて使えるコマンドが増えています
詳細については，
```bash
ipdb> h

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

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

Undocumented commands:
======================
interact
```
コマンドの詳細を確認したいときは以下で確認できます．
```bash
ipdb> help <COMANND>
```

## pdb++について
最後にpdb++についてです．

- [GitHubリポジトリ: pdbpp/pdbpp](https://github.com/pdbpp/pdbpp)

pdb++もipdb同様別途インストールが必要です．
```bash
$ pip install pdbpp
```
pdb++はインストールするとpdbを置き換えてくれるので使用する際は，`import pdb`のままで使うことができます．
pdb++をインストールした後，最初に示した例を実行すると以下のようにデバッガが色づけられ，`(Pdb)` -> `(Pdb++)`になっていることが確認できます． 

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624265/b919cca7-cf24-9808-5346-251937cd2d11.png width=480>

- pdb++ではipdbに加えて以下の特徴が挙げられます

## 1. コマンドが増える
```bash
(Pdb++) help

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

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

Undocumented commands:
======================
f  frame  hf_list  inspect  paste  put  source
```
詳細については，
```bash
(Pdb++) help <COMMAND>
```
やGitHubリポジトリを参照してください．

## 2. コマンドパーサーが賢い

pdb++では，コマンドと変数
例えば，`mymodule.py`を以下のように変更したとします．
```python:mymodule.py
import pdb


def test():
    c = 10
    print("Hello World1")
    print("Hello World2")
    print("Hello World3")
    print("Hello World4")
    print("Hello World5")
    print(f"c = {c}")
    pdb.set_trace()
    return c
```
pdbの場合では，ブレークポイント後`c(continue)`を押すとそのまま処理が実行されますが，pdb++では変数cの値を返してくれます．
再びcontinueを実行したい場合は`!!c`で処理を再実行します．

```bash
$ python3 main.py
[1] > <string>(1)<module>()
(Pdb++) c
Hello World1
Hello World2
Hello World3
Hello World4
Hello World5
c = 10
[4] > /home/ktro2828/my_project/my_project/mymodule.py(13)test()
-> return c
(Pdb++) c
10
(Pdb++) c
10
(Pdb++) !!c  # !!cでcontinueを実行
```

# おわりに
今回は，python標準デバッガであるpdbとその拡張版であるipdb/pdb++について紹介しました．どのライブラリを使うかは個人の好みでいいかと思いますが，（自分も含めて）これらのツールを使ってより効率的に開発を進めていきたいですね！