概要
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
の中身は実は以下でした。超絶単純ですね。
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 ファイル名:行番号
とすることで、現在デバッグ中とは別のファイルにブレークポイントを設定することもできます。