Python開発:「pdb」を使って効率的なデバッグをしよう！

# はじめに
プログラミングにおいて**デバッグ作業**は避けては通れない道です。
Pythonを初めて学ぶ際には、print文を使用してデバッグする方法が一般的ですが、これに加えてPythonにはより効果的なデバッグ手法があります。
その1つが、Python標準ライブラリに含まれる`pdbモジュール`を使用したデバッグです。

https://docs.python.org/ja/3/library/pdb.html

## PDBとは何か？
**PDB**は**Python標準デバッガ**であり、Pythonプログラムのデバッグを支援するためのツールです。具体的には、以下のような機能を提供しており柔軟なデバッグが可能で効率的に問題の特定や解決ができます。
1. ブレークポイントの設定: プログラムの特定の行で実行を一時停止させることができます。これにより、プログラムの特定のポイントで状態を調査したり、問題の原因を特定したりすることができます。
2. シングルステップ実行: プログラムを1つのステップずつ実行し、各ステップでの変数の値やプログラムの状態を確認することができます。これにより、プログラムの動作を詳細に理解することができます。
3. スタックフレームのインスペクション: 実行中のプログラムのスタックトレースを表示し、各スタックフレームでの変数の値や関数の呼び出し履歴を調べることができます。
4. ソースコードリスティング: プログラムの実行中にソースコードを表示し、どの行が実行されているかを確認することができます。これにより、どの部分のコードが問題を引き起こしているかを特定できます。
5. Pythonコードの評価: 実行中のプログラムの任意のスタックフレームでPythonコードを評価し、変数の値を確認したり、特定の処理を行ったりすることができます。
pdbの使用方法

今回はサンプルでランダムなジョークが取得できる一風変わったAPI(https://icanhazdadjoke.com/api)を使用したコードを例に解説します

https://icanhazdadjoke.com/

実装コード↓
```python
import requests

def print_joke():
    url = "https://icanhazdadjoke.com/"

    headers = {"Accept": "application/json"}

    response = requests.get(url=url, headers=headers)
    if response.status_code == 200:
        joke = response.json()["joke"]
        breakpoint()
        print(joke)
    else:
        print(response.status_code)

if __name__ == "__main__":
    print_joke()
```



pdbの使用方法はデバッグしたい箇所に`breakpoint()`を記入し、Pythonファイルを実行すると対話的なデバッグを開始できます。

:::note info
補足
`breakpoint()`はPython3.7で追加されたため、それ以前のバージョンでは`import pdb; pdb.set_trace()`をbreakpointの代わりに挿入すると使用できます。
:::

pdbの画面に入ると`(Pdb)`と表示されpdbコマンドとPythonコードが実行可能になります。
**pdbコマンド**を利用することで対話的にPythonコードをpdb内で実行・確認できるます！
```bash
(Pdb) import os
(Pdb) text = response.text
(Pdb) p text
'{"id":"xAlOZ8pOmjb","joke":"How do the trees get on the internet? They log on.","status":200}\n'
```

## pdbの基本コマンド解説
pdbにはさまざまなコマンドがありますが、今回はよく使用するコマンドをピックアップして解説します。
全てのコマンド見る場合は[ドキュメント](https://docs.python.org/ja/3/library/pdb.html)を参照してください。

https://docs.python.org/ja/3/library/pdb.html

## l(list)
現在のbreakpointが止まっている前後11行分のソースコードを表示
現在処理が止まっている箇所は -> で確認可能
`l <行番号>`のようにすると指定した行の前後も表示可能
```bash
-> print(joke)
(Pdb) l
  8  
  9         response = requests.get(url=url, headers=headers)
 10         if response.status_code == 200:
 11             joke = response.json()["joke"]
 12             breakpoint()
 13  ->         print(joke)
 14         else:
 15             print(response.status_code)
 16  
 17  
 18     if __name__ == "__main__":
 ```

## ll(longlist)
現在の関数またはフレームの全ソースコードを表示
```bash
(Pdb) ll
  4     def print_joke():
  5         url = "https://icanhazdadjoke.com/"
  6  
  7         headers = {"Accept": "application/json"}
  8  
  9         response = requests.get(url=url, headers=headers)
 10         if response.status_code == 200:
 11             joke = response.json()["joke"]
 12             breakpoint()
 13  ->         print(joke)
 14         else:
 15             print(response.status_code)
```

## c(continue)
次のbreakpointまで実行を継続

## r(return)
現在の関数が返るまで実行を継続

## j(jump)
次に実行する行を指定可能前に戻ったり不要な行をスキップして処理を実行する場合に使用

## s(step)
現在の行を実行し、最初に実行可能なものがあらわれたときに停止

## n(next)
現在の関数の次の行に達するか、あるいは関数が返るまで実行を継続

## unt(until)
unt 指定行で指定行まで処理を実行可能引数なしだと現在の行から1行先まで実行

## a(args)
breakpointで実行している現在の関数の引数(引数の値が上書きされている場合は現在の値を出力)

## p(print)
変数やメソッドなどを評価しその値をプリント
```bash
(Pdb) p response.json()
{'id': 'oOKBsPZ0wc', 'joke': 'I am so good at sleeping I can do it with my eyes closed!', 'status': 200}```
```

## pp(prety-print)
pprintを使用してデータを見やすくプリント
```bash
(Pdb) pp response.json()
{'id': 'oOKBsPZ0wc',
 'joke': 'I am so good at sleeping I can do it with my eyes closed!',
 'status': 200}
 ```

## h(help)
利用できるコマンド一覧を表示help コマンド名でコマンドのドキュメント(docstring)を表示
```bash
(Pdb) h
Documented commands (type help <topic>):
========================================
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
```

## b(break)
`bファイル名:行数 or 関数名`で新たにbreakpointを指定して追加可能
現在の実行ファイルにbreakpointを追加する場合はファイル名の指定は無しでも可能
breakpointはl(list)やll(longlist)で位置が確認可能
```bash
(Pdb) b 13
Breakpoint 1 at /Users/sakamoto/pdb/sample.py:13
(Pdb) ll
  4     def print_joke(data):
  5         url = "https://icanhazdadjoke.com/"
  6  
  7         headers = {"Accept": "application/json"}
  8         breakpoint()
  9  
 10  ->     response = requests.get(url=url, headers=headers)
 11         if response.status_code == 200:
 12             joke = response.json()["joke"]
 13 B           print(data)
 14             data = 2
 15             print(joke)
 16         else:
 17             print(response.status_code)
 ```

## q(quit) または exit
デバッガーを終了。実行しているプログラムは中断されます。

# 最後に
今回はPython標準のデバッガである**pdb**についてご紹介しました。
慣れれば、pdbなしでは開発が考えられないほど便利なツールです！
pdbを活用して、より堅牢で信頼性の高いPythonプログラムを開発できることを願っています。
ご拝読いただきありがとうございました🦭