211:pythonスクリプトをデバッグする
Python デバッガ pdb
Python には pdb というデバッガが付属しています。
例として次のようなコード spam.py をデバッグしてみます。 x の初期値を 6 とし 3, 2, 1, 0 で割り算していきます。 x の値は for 文が回るたびに 2, 1, 1 となり、4 周目の 0 で割り算された時点で例外 ZeroDivisionError? が発生します。 この例外は処理されることは無く、異常終了します。
# coding: utf-8
def div(x, y):
return x / y
def main():
x = 6
for i in [3, 2, 1, 0]:
x = div(x, i)
return x
if __name__ == '__main__':
main()
デバッグを開始する
スクリプトのデバッグを開始するには、次のようにします。
C:\spam>python -m pdb spam.py > c:\spam\spam.py(3)<module>() -> def div(x, y): (Pdb)
インタラクティブ上から、起動することもできます。 pdb.run メソッドに実行したいコードを文字列で渡します。
>>> import pdb
>>> import spam
>>> pdb.run('spam.main()')
> <string>(1)<module>()
(Pdb)
また、デバッガを立ち上げてはいないときに、異常終了に遭遇した場合にも、後からデバッガを立ち上げて原因の調査を行うことが可能です。 pdb.pm 関数を使用することで事後解析モードで立ち上がります。
>>> import spam
>>> spam.main()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "spam.py", line 9, in main
x = div(x, i)
File "spam.py", line 4, in div
return x / y
ZeroDivisionError: integer division or modulo by zero
>>> import pdb
>>> pdb.pm()
> c:\spam\spam.py(4)div()
-> return x / y
(Pdb)
プログラムを進める
C:\spam>python -m pdb spam.py > c:\spam\spam.py(3)<module>() -> def div(x, y):
としてスクリプトのデバッグをはじめたものとします。
pdb プロンプトが立ち上がった直後は、一番最初の文である 3 行目の def div(x, y') で動作が止まっています。
現在箇所の周辺のソースコードを確認するには list コマンドを用います。
(Pdb) list 1 # coding: utf-8 2 3 -> def div(x, y): 4 return x / y 5 6 def main(): 7 x = 6 8 for i in [3, 2, 1, 0]: 9 x = div(x, i) 10 return x 11
現在箇所周囲のソースコードが表示されます。現在箇所は -> で示されます。
プログラムの実行を進めるには step コマンドを使います。
(Pdb) step > c:\spam\spam.py(6)<module>() -> def main():
div 関数の定義が実行され、次の文である 6 行目の def main() に移動しました。さらに進めます。ほとんどのコマンドは最初の一文字でも動作します。 step コマンドの場合は s です。
(Pdb) s > c:\spam\spam.py(12)<module>() -> if __name__ == '__main__':
12 行目の if 文に到達しました。何度も同じコマンドを実行する場合はただ Enter キーを押すだけでいいようになっています。前回入力した step コマンドが再度実行されます。
(Pdb) > c:\spam\spam.py(13)<module>() -> main() (Pdb) --Call-- > c:\spam\spam.py(6)main() -> def main():
12 行目の main() に到達し、 main 関数が呼び出されました。これにより 6 行目に飛びました。
一行ずつ進めるコマンドのほかに、今の関数が終了し帰るまで続行してくれるコマンドもあります。 return コマンドがそうです。 ここで return をおこなうと、 main 関数が終了するまで進められます。
(Pdb) return --Return-- > c:\spam\spam.py(9)main()->None -> x = div(x, i)
None が帰ってきました!? さらに進めてみることにします。
プログラムの最後まで進めてしまうコマンドもあります(正確にはブレークポイントまでです、後述)。 continue コマンドです。
(Pdb) continue
Traceback (most recent call last):
File "C:\python26\lib\pdb.py", line 1283, in main
pdb._runscript(mainpyfile)
File "C:\python26\lib\pdb.py", line 1202, in _runscript
self.run(statement)
File "C:\python26\lib\bdb.py", line 368, in run
exec cmd in globals, locals
File "<string>", line 1, in <module>
File "spam.py", line 13, in <module>
main()
File "spam.py", line 9, in main
x = div(x, i)
File "spam.py", line 4, in div
return x / y
ZeroDivisionError: integer division or modulo by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> c:\spam\spam.py(4)div()
-> return x / y
ゼロ除算例外で異常終了し、トレースバックの事後解析モードに入りました。 例外の送出先である 4 行目に移動しています。
状況の確認
現在箇所を確認するため、先ほども出てきた list コマンドを実行してみます。
(Pdb) list 1 # coding: utf-8 2 3 def div(x, y): 4 -> return x / y 5 6 def main(): 7 x = 6 8 for i in [3, 2, 1, 0]: 9 x = div(x, i) 10 return x 11
現在の箇所は -> で示され 4 行目であることがわかります。
この時点での変数の内容を確認するには p コマンドを用います。
(Pdb) p x 1 (Pdb) p y 0
この時点での div 関数内の変数 x が 1 、 y が 0 であることがわかります。 y が 0 のためゼロ除算エラーが起こったことが予想されます。
スタックトレースを表示するには where コマンドを使用します。
(Pdb) where c:\python26\lib\pdb.py(1283)main() -> pdb._runscript(mainpyfile) c:\python26\lib\pdb.py(1202)_runscript() -> self.run(statement) c:\python26\lib\bdb.py(368)run() -> exec cmd in globals, locals <string>(1)<module>() c:\spam\spam.py(13)<module>() -> main() c:\spam\spam.py(9)main()->None -> x = div(x, i) > c:\spam\spam.py(4)div() -> return x / y
> で示されている x = div(x, i) の箇所が現在のフレームです。 フレームを移動するには up, down コマンドを用います。
(Pdb) up > c:\spam\spam.py(9)main()->None -> x = div(x, i) (Pdb) l 4 return x / y 5 6 def main(): 7 x = 6 8 for i in [3, 2, 1, 0]: 9 -> x = div(x, i) 10 return x 11 12 if __name__ == '__main__': 13 main() [EOF] (Pdb) p x 1 (Pdb) p i 0
up コマンドで一つ古いフレームに移動しました。 main 関数内の変数 x と i の値がそれぞれ 1, 0 であることがわかります。
デバッガを終了するには quit コマンドを使用します。
(Pdb) quit Post mortem debugger finished. The spam.py will be restarted > c:\spam\spam.py(3)<module>() -> def div(x, y): (Pdb) C:\spam>
ブレークポイントの設置
step, next, return コマンドでプログラムを進めるのは手間がかかります。かといって continue コマンドを使用すると プログラムは最後まで走ってしまいます。確認したい箇所がわかっている場合、そこにブレークポイントを設置するとよいでしょう 各種コマンドでプログラムが進んでいる最中でも、ブレークポイントに到達するとデバッガは処理を中断し、そこで停止します。
ブレークポイントを設置するには break コマンドを用います。 9 行目にブレークポイントを設置するには次のようにします。
C:\spam>python -m pdb spam.py > c:\spam\spam.py(3)<module>() -> def div(x, y): (Pdb) break 9 Breakpoint 1 at c:\spam\spam.py:9
この状態では continue コマンドを実行しても 9 行目に到達した時点で停止してくれるようになります。
(Pdb) continue > c:\spam\spam.py(9)main() -> x = div(x, i) (Pdb) p i, x (3, 6)
初めて到達した時点での main 関数内 x, i 変数の値が 3, 6 であることがわかります。
(Pdb) continue > c:\spam\spam.py(9)main() -> x = div(x, i) (Pdb) p i, x (2, 2)
2度目の到達時点での x, i の値が 2, 2 であることがわかります。
設定したブレークポイントを確認しなおすには break コマンドを引数なしで実行します。
(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at c:\spam\spam.py:9
breakpoint already hit 2 time
1 番のブレークポイントが spam.py の 9 行目にあり、このブレークポイントには 2 回到達したことがわかります。
設定したブレークポイントを一時無効にするには disable, 有効にするには enable コマンドを用います。削除するには clear コマンドを用います。
(Pdb) clear 1 Deleted breakpoint 1