Pythonデバッグ方法 入門編

## 概要

Pythonプログラムのデバッグ、どうやっていますか？ここではpdbを利用したPythonデバッグの基礎中の基礎をご紹介します。

## 前提条件

* Python 2.7

> 筆者はPython 3.x系にはほとんど触れたことがないので3.x系には触れません。が、3.x系もほとんど共通しているかもしれません。

## Pythonデバッガ
### 基礎
pythonは[pdbモジュール](https://docs.python.org/ja/2.7/library/pdb.html)を利用することで以下のようなデバッグ機能を利用することができます。

* ステップ実行
* ブレークポイントの設定

> その他、スタックフレームのインスペクション、ソースコードリスティングおよびあらゆるスタックフレームのコンテキストにおける任意の Python コードの評価をサポートしています。事後解析デバッギングもサポートし、プログラムの制御下で呼び出すことができます。

まず通常のPythonスクリプトの実行方法は以下です。これは誰でも知っている書式ですね。

```python:実行例
python myscript.py
```

次にデバッグ実行時の書式は以下です。

```python:実行例
python -m pdb myscript.py
```

なにやら`-m`というオプションと共にpdbという引数が渡されました。これがデバッグ実行のためのおまじないとなります。

>python実行時に`-m`オプションに続けてモジュール名（ここではpdb）を指定すると、そのモジュールをスクリプトとして実行します[^cmdline]。つまり、はじめに呼び出されるのはpdbというモジュール（pythonスクリプト)で、pdbモジュールの中でpythonコマンドの引数に渡した`myscript.py`が読み込まれます。pdbは標準モジュールなのでインストール等は不要となります。

> pdbは"**P**ython **D**e **B**ugger"の略称？と覚えると良いかもしれません。

さて、`myscript.py`の中身は実は以下でした。超絶単純ですね。

```python:myscript.py
def sum(a,b):
    result = a+b
    return result

a = 10
b = 20
print "Answer is {}!!!".format(sum(a,b))
```

普通に実行してみます。

```bash
$ python myscript.py 
Answer is 30!!!
```

最後のprint文の実行結果である`30`が表示されました。

次に`-m`オプションと共にpdbモジュールでデバッグ実行してみます。

```bash
$ python -m pdb myscript.py 
> /path/to/your/file/myscript.py(1)<module>()
-> def sum(a,b):
(Pdb) 
```

おや？さきほどと挙動が違いますね。最終行には(Pdb)というプロンプトが表示され、何やら(Pdb)に続けて文字列を入力することができます。

実はこれ`myscript.py`の1行目(ここでは`def sum(a,b)`)が読み込まれ、実行待機状態となっています。ここでデバッガコマンドを実行することにより、このファイルを1行ずつ実行したり、ブレークポイントを設定したりすることができます。

主なデバッガコマンドを以下に示します。コマンドによっては引数を指定可能となるものもあります。全てのコマンドや引数については[26.3. デバッガコマンド](https://docs.python.org/ja/2.7/library/pdb.html#debugger-commands)を参照してください。

**デバッガコマンド**

|command|説明|
|:--|:--|
|s(tep)|1行実行します。関数の中に入ります。|
|n(ext)|1行実行します。関数の中には入りません。|
|c(ont(inue))|ブレークポイントに出会うまで実行を継続します。|
|b(reak) 行番号|現在のファイルの行番号にブレークポイントを設定します。この他、ファイル名や関数名、条件式も指定することができます。|
|l(ist)|現在のファイルのソースコードを表示します。引数により表示範囲を広げることができます。現在実行している行の周りのソースコードを確認したいときに使います。|
|h(elp)|デバッガコマンドのヘルプ。コマンドを忘れたときに使います。|
|q(uit)|デバッガを終了します。|

**Python文の実行**
デバッガコマンド以外のコマンド（例：`print a`など)は、通常のPython文とみなして、デバッグ中のプログラムの行（コンテキスト）において実行されます。これにより、ある行での変数の状態を確認したり、あるいは変数の中身を変更することができます。

**コマンドの省略**
空行(コマンドなしで改行)を入力すると、直前のコマンドを再実行します。これにより、`s`を一度実行すれば、あとは都度`s`を入力しなくともEnterキーを入力するだけで1行ずつステップ実行することができます。

### ステップ実行
ではスクリプトをステップ実行し、ある時の変数の状態を確認してみます。まずは`-m pdb`を指定してスクリプトを実行します。

```bash
$ python -m pdb myscript.py 
> /path/to/your/file/myscript.py(1)<module>()
-> def sum(a,b):
(Pdb) 
```

`->`が現在の実行対象行を表しています。`def`なので関数sumの読み込み直前ですね。このまま実行してしまいましょう。次の行に進むには`next`コマンドでした。

```
(Pdb) next
> /path/to/your/file/myscript.py(5)<module>()
-> a = 10
```

次の実行可能な行(5行目)である`a = 10`まできました。続けて次の行に進みましょう。もう一度`next`を実行しても良いのですが、同じコマンドを再実行する場合はそのままEnterを入力してもOKです。

```
(Pdb) 
> /path/to/your/file/myscript.py(6)<module>()
-> b = 20
```

6行目`b = 20`まできました。続けてそのままEnterを入力し、次の行に行きましょう。

```
(Pdb) 
> /path/to/your/file/myscript.py(7)<module>()
-> print "Answer is {}!!!".format(sum(a,b))
```

いよいよ関数の実行部分まできました。このままEnterを押すと直前に実行したコマンド`next`が実行され、sum関数の実行結果が返ってきてプログラムが終わってしまいます。そこで、ここでは関数の中に入るために`next`ではなく、`step`コマンドを実行してみます。

```
(Pdb) step
--Call--
> /path/to/your/file/myscript.py(1)sum()
-> def sum(a,b):
```

`print`の引数にあるsum関数が呼ばれました。続けて`step`を実行します。同じコマンドなのでEnterでOKでしたね。

```
(Pdb) 
> /path/to/your/file/myscript.py(2)sum()
-> result = a+b
```

2行目の`result = a+b`まできました。そのままEnterで実行してしまいましょう。

```
(Pdb) 
> /path/to/your/file/myscript.py(3)sum()
-> return result
```
`return`まできました。ここではすでに`result`の値が入っているはずです。確認してみましょう。

```
(Pdb) result
30
```

`result`には`a+b`の結果`30`が格納されていることがわかりました。では、もう最後まで一気に実行してしまいましょう。
`continue`コマンドを実行します。

```
(Pdb) continue
Answer is 30!!!
The program finished and will be restarted
> /path/to/your/file/myscript.py(1)<module>()
-> def sum(a,b):
```

`Answer is 30!!!`が出力され、スクリプトが一旦終了しました。ただし、pdbはスクリプトの最後まで到達すると自動的にスクリプトを再実行するため、もう一度1行目から始まっていることがわかります。

デバッグを終了するため`quit`を入力します。

```
(Pdb) quit
```

### ブレークポイントの設定

次にブレークポイントの設定をやり方を説明します。先ほどは1行ずつ実行していきましたが、今度は指定した行まで一気に実行してしまいます。では先ほどと同じように`-m pdb`オプションを指定してスクリプトを実行します。

```
$ python -m pdb myscript.py 
> /path/to/your/file/myscript.py(1)<module>()
-> def sum(a,b):
(Pdb) 
```

1行目が表示されました。ここで`continue`を実行するとブレークポイントが設定されていないので一番最後まで実行され、スクリプトが終了してしまいます。そこで、ここではsum関数の中にある`return result`にブレークポイントを設定します。

ところで`return result`は何行目でしたっけ？そんな時は`list`コマンドです。

```
(Pdb) list
  1  ->	def sum(a,b):
  2  	    result = a+b
  3  	    return result 
  4  	
  5  	a = 10
  6  	b = 20
  7  	print "Answer is {}!!!".format(sum(a,b))
[EOF]
```

現在の実行中の行の周辺のソースコードが表示されます。`return result`は3行目にありますね。3行目にブレークポイントを設定します。

> 表示範囲は`list`コマンドの引数で指定することもできます。書式は`list 開始行,終了行`です。引数なしの場合は、現在の行の周囲を11行リストするか、または前のリストの続きを表示します。

ブレークポイントの設定は`break 行番号`にて指定します。今回は3行目なので`break 3`となります。

```
(Pdb) break 3
Breakpoint 1 at /path/to/your/file/myscript.py:3
```

ブレークポイントは本当に設定されたのでしょうか？設定されたブレークポイント一覧を確認するには引数なしの`break`コマンドです。

```
(Pdb) break
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at /path/to/your/file/myscript.py:3
```
myscript.pyの3行目にブレークポイントが設定されていることがわかります。ではブレークポイントまで一気に実行してみましょう。`continue`コマンドを省略した`c`を入力します。

```
(Pdb) c
> /path/to/your/file/myscript.py(3)sum()
-> return result
```

3行目に到達したところでデバッガが止まりました。ついでに`list`コマンド(省略形は`l`)で周辺のソースコードも確認しておきましょう。

```
(Pdb) l
  1  	def sum(a,b):
  2  	    result = a+b
  3 B->	    return result 
  4  	
  5  	a = 10
  6  	b = 20
  7  	print "Answer is {}!!!".format(sum(a,b))
[EOF]
```

ブレークポイントが設定された行には`B`マークが表示され、3行目で止まっていることが確認できました。せっかくなのでresultの結果を書き換えちゃいましょう。好きなpython文を実行することができます。

```
(Pdb) result = 100
```

そのまま`continue`で最後まで実行します。

```
(Pdb) c
Answer is 100!!!
The program finished and will be restarted
```

resultの値が書き換えられ、`Answer is 100!!!`が出力されました。

以上が基本的なpdbの使い方となります。デバッガコマンドはここで紹介した以外にもいくつかあり、引数を指定して使い方を拡張できるものもあるので公式ページを確認してみてください。[^pdb]

> 例えば`break ファイル名：行番号`とすることで、現在デバッグ中とは別のファイルにブレークポイントを設定することもできます。

## 参考
[^cmdline]: [コマンドラインと環境](https://docs.python.org/ja/2.7/using/cmdline.html)

[^pdb]: [Python デバッガ](https://docs.python.org/ja/2.7/library/pdb.html)