Edited at

Pythonデバッグ方法 入門編

More than 1 year has passed since last update.


概要

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


前提条件


  • Python 2.7


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



Pythonデバッガ


基礎

pythonはpdbモジュールを利用することで以下のようなデバッグ機能を利用することができます。


  • ステップ実行

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


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


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


実行例

python myscript.py


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


実行例

python -m pdb myscript.py


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


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

pdbは"Python De Bugger"の略称?と覚えると良いかもしれません。


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


myscript.py

def sum(a,b):

result = a+b
return result

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


普通に実行してみます。

$ python myscript.py 

Answer is 30!!!

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

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

$ 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. デバッガコマンドを参照してください。

デバッガコマンド

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を指定してスクリプトを実行します。

$ 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の使い方となります。デバッガコマンドはここで紹介した以外にもいくつかあり、引数を指定して使い方を拡張できるものもあるので公式ページを確認してみてください。2


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



参考