このチュートリアルでは、PythonのPDBモジュールを使ってPythonアプリケーションをデバッグする方法について学びます。デバッグとは、ソフトウェアアプリケーションからソフトウェアやハードウェアのエラーを除去するプロセスのことです。PDBは「Python Debugger」の略で、プログラムの一時停止、特定のインスタンスでの変数値の表示、それらの値の変更など、幅広い機能を持つ組み込みの対話型ソースコードデバッガです。
この記事では、PDBモジュールの最も一般的に使用される機能を取り上げる予定です。
背景
デバッグは、ソフトウェア開発において最も嫌われる作業の一つであると同時に、ソフトウェア開発のライフサイクルにおいて最も重要な作業の一つである。非常に基本的なソフトウェアアプリケーションを開発している場合を除き、すべてのプログラマは自分のコードをデバッグしなければならない段階がある。
ソフトウェアアプリケーションをデバッグする方法には、多くの異なる方法があります。非常によく使われる方法は、コードの異なるインスタンスで「print」ステートメントを使用して、実行中に何が起こっているかを確認する方法です。しかし、この方法は、変数の値を表示するために余分なコードが追加されるなど、多くの問題があります。この方法は小さなプログラムでは有効かもしれませんが、多くのコード行を持つ大規模なアプリケーションで、異なるファイルにまたがってこれらのコード変更を追跡することは、大きな問題になりかねません。デバッガはその問題を解決してくれます。デバッガは、外部コマンドを使用して、アプリケーションのエラーソースを見つけるのを助けてくれます。
注意: 前述のように、PDBはPythonの組み込みモジュールなので、外部からインストールする必要はありません。
キーコマンド
PDBで自由に使える主なコマンドやツールを理解するために、基本的なPythonプログラムを考え、それをPDBのコマンドを使ってデバッグしてみましょう。各コマンドが具体的に何をするのか、例を挙げて見ていきましょう。
# Filename: calc.py
operators = ['+', '-', '*', '/']
numbers = [10, 20]
def calculator():
print("Operators available: ")
for op in operators:
print(op)
print("Numbers to be used: ")
for num in numbers:
print(num)
def main():
calculator()
main()
上のスクリプトの出力は以下のとおりです。
コード偽
上のコードは初心者にやさしく、複雑な概念や構文がまったく含まれていないので、コメントは一切つけていない。このコードが達成する「タスク」を理解することは重要ではありません。このコードの目的は、PDBのすべてのコマンドをこの上でテストできるように特定のものを含めることだからです。それでは、始めましょう
PDBを使うにはコマンドラインインターフェイス(CLI)を使う必要があるので、アプリケーションはターミナルかコマンドプロンプトから実行しなければなりません。
CLIで以下のコマンドを実行してください。
Operators available:
+
-
*
/
Numbers to be used:
10
20
上のコマンドでは、私のファイル名は “calc.py” なので、ここにあなたの好きなファイル名を入れてください。
Note: -m
はフラグで、モジュールをインポートする必要があることをPythonの実行ファイルに通知します。このフラグの後に、モジュールの名前(この例では pdb
)が続きます。
このコマンドの出力は以下のようになります。
$ python -m pdb calc.py
出力は常に同じ構造をしています。出力は常に同じ構造で、ソースコード・ファイルのディレクトリ・パスから始まります。次に、PDBが現在参照しているファイルの行番号を括弧で囲んで表示します(この例では「(3)」です)。次の行は「-」記号で始まり、現在指し示している行を表しています。
PDB プロンプトを閉じるには、単に quit
または exit
を PDB プロンプトに入力します。
他にもいくつか注意点があります。もし、プログラムが入力としてパラメータを受け取る場合、コマンドラインからパラメータを渡すことができます。例えば、上記のプログラムがユーザーから3つの入力を要求していたとしたら、コマンドは以下のようになります。
> /Users/junaid/Desktop/calc.py(3)<module()
-> operators = [ '+', '-', '*', '/' ]
(Pdb)
次に、quit
または exit
コマンドでPDBプロンプトを閉じた場合は、PDBでコードファイルを再実行してください。その後、PDBプロンプトで次のコマンドを実行してください。
$ python -m pdb calc.py var1 var2 var3
出力はこのようになります。
(Pdb) list
これはあなたのプログラムの最初の11行を表示します。”-” はデバッガによって実行されている現在の行を指しています。次に、PDBプロンプトで次のコマンドを試してみてください。
1 # Filename: calc.py
2
3 -> operators = ['+', '-', '*', '/']
4 numbers = [10, 20]
5
6 def calculator():
7 print("Operators available: ")
8 for op in operators:
9 print(op)
10
11 print("Numbers to be used: ")
(Pdb)
このコマンドは選択された行(この場合は4行目から6行目)だけを表示するはずです。 以下がその出力です。
(Pdb) list 4,6
ブレークポイントを利用したデバッグ
次に重要なことは、ブレークポイントについて学ぶことです。ブレークポイントは通常より大規模なプログラムで使用されますが、よりよく理解するために、この基本的な例でどのように機能するかを見てみましょう。ブレークポイントとは、コードの中で宣言する特定の位置のことです。コードはその位置まで実行され、その後一時停止します。これらのポイントには、PDBによって自動的に番号が割り当てられます。
ブレークポイントを作成するには、次のようなオプションがあります。
- 行番号で指定する
- 関数宣言で
- 条件によるもの
行番号でブレークポイントを宣言するには、PDBプロンプトで次のコマンドを実行します。
4 numbers = [10, 20]
5
6 def calculator():
(Pdb)
このコマンドは、8行目のコードにブレークポイントを挿入し、そのポイントに到達するとプログラムを一時停止させます。このコマンドの出力は次のように表示されます。
(Pdb) break calc.py:8
関数にブレークポイントを宣言するには、PDBプロンプトで次のコマンドを実行します。
Breakpoint 1 at /Users/junaid/Desktop/calc.py: 8
(Pdb)
この方法でブレークポイントを挿入するには、ファイル名と関数名を使用してブレークポイントを宣言する必要があります。これは次のように出力されます。
(Pdb) break calc.calculator
見ての通り、このブレークポイントには自動的に2番が割り当てられ、関数が宣言されている行番号(6)も表示されています。
ブレークポイントは、条件によって宣言することもできます。この場合、プログラムはその条件が偽になるまで実行され、その条件が真になると一時停止します。PDBプロンプトで次のコマンドを実行します。
Breakpoint 2 at /Users/junaid/Desktop/calc.py:6
このコマンドは実行中ずっと変数op
の値を追跡し、8行目でその値が「 * 」になったときだけブレークします。
宣言したすべてのブレークポイントをリスト形式で見るには、PDBプロンプトで次のコマンドを実行してください。
(Pdb) break calc.py:8, op == "*"
出力はこのようになります。
(Pdb) break
最後に、特定のブレークポイントを無効にしたり、有効にしたり、クリアしたりする方法を説明します。PDBプロンプトで次のコマンドを実行します。
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/junaid/Desktop/calc.py: 8
2 breakpoint keep yes at /Users/junaid/Desktop/calc.py: 6
breakpoint already hit 1 time
3 breakpoint keep yes at /Users/junaid/Desktop/calc.py: 8
stop only if op == "*"
(Pdb)
このコマンドはブレークポイント 2 を無効にしますが、デバッガーのインスタンスからは削除されません。
出力には、無効化されたブレークポイントの番号が表示されます。
(Pdb) disable 2
もう一度すべてのブレークポイントのリストを出力して、ブレークポイント 2 の “Enb” 値を確認しましょう。
Disabled breakpoint 2 at /Users/junaid/Desktop/calc.py:6
(Pdb)
出力します。
出力: “`
(Pdb) break
ブレークポイント2を再び有効にするには、次のコマンドを実行します。
Num Type Disp Enb Where
1 breakpoint keep yes at /Users/junaid/Desktop/calc.py:8
2 breakpoint keep no at /Users/junaid/Desktop/calc.py:4 # you can see here that the “ENB” column for #2 shows “no”
breakpoint already hit 1 time
3 breakpoint keep yes at /Users/junaid/Desktop/calc.py:8
stop only if op == “*”
(Pdb)
そして再び、次のような出力が得られます。
(Pdb) enable 2
ここで、すべてのブレークポイントのリストをもう一度印刷すると、ブレークポイント2の「Enb」列の値が再び「yes」と表示されるはずです。
ここで、ブレークポイント1をクリアして、すべて削除してしまいましょう。
Enabled breakpoint 2 at /Users/junaid/Desktop/calc.py:6
出力は次のようになります。
(Pdb) clear 1
ブレイクポイントのリストを印刷し直すと、2つのブレイクポイントの列だけが表示されるはずです。break" コマンドの出力を見てみましょう。
Deleted breakpoint 1 at /Users/junaid/Desktop/calc.py:8
(Pdb)
まさに期待通りの結果です。
このセクションから先に進む前に、指定したブレークポイントまで実際にコードを実行したときに表示される内容を皆さんにお見せしたいと思います。そのために、これまでのブレークポイントをすべてクリアして、PDBプロンプトから別のブレークポイントを宣言してみましょう。
1. すべてのブレークポイントをクリアする
Num Type Disp Enb Where
2 breakpoint keep yes at /Users/junaid/Desktop/calc.py:4
breakpoint already hit 1 time
3 breakpoint keep yes at /Users/junaid/Desktop/calc.py:8
stop only if op == “*”
その後、"y "をタイプして "Enter "を押してください。このような出力が表示されるはずです。
(Pdb) clear
2. 新しいブレークポイントを宣言する
私たちが実現したいのは、変数 `num` の値が 10 よりも大きくなるまでコードを実行することです。つまり、基本的にプログラムは "20" という数字が出力される前に一時停止する必要があります。
Deleted breakpoint 2 at /Users/junaid/Desktop/calc.py:6
Deleted breakpoint 3 at /Users/junaid/Desktop/calc.py:8
3. このブレークポイントまでコードを走らせる
コードを実行するには、"continue "コマンドを使ってください。
(Pdb) break calc.py:13, num 10
次のような出力が表示されるはずです。
(Pdb) continue
これは期待通りの結果で、プログラムはこの時点まで実行され、その後一時停止します。あとは、何かを変更するか、変数を検査するか、スクリプトを完了するまで実行するか、私たち次第です。完了まで実行するように指示するには、もう一度 "continue "コマンドを実行してください。次のような出力が得られるはずです。
Operators available:
+
–
*
/
Numbers to be used:
10
/Users/junaid/Desktop/calc.py(13)calculator()
– print(num)
上の出力では、プログラムが中断したところから継続して、残りの部分を実行し、さらにデバッグするために再スタートしていることがわかる。では、次のセクションに進みましょう。
重要:先に進む前に、「clear」コマンドを実行し、PDBプロンプトに「y」を入力して、すべてのブレークポイントをクリアしてください。
### 次へ」「ステップ」機能
最後に、 `next` と `step` 関数について勉強しましょう。これらはアプリケーションのデバッグを始めるときに非常に頻繁に使用されます。
step` と `next` 関数は、コードを一行ずつ繰り返し処理するために使用されます。この2つの間にはほとんど違いはありません。一方、 `next` 関数が関数呼び出しに遭遇した場合、その関数のすべての行を一度に実行し、次の関数呼び出しのところで一時停止します。
混乱しましたか?例で見てみましょう。
PDBプロンプトから次のコマンドでプログラムを再実行します。
20
The program finished and will be restarted
/Users/junaid/Desktop/calc.py(3)
– operators = [ ‘+’, ‘-‘, ‘*’, ‘/’ ]
PDBプロンプトで `continue` と入力し、プログラムが終了するまでこれを続けてください。以下に、入出力シーケンス全体の一部を示しますが、これはポイントを説明するのに十分です。全部のシーケンスはかなり長く、さらに混乱させるだけなので省略します。
$ python -m pdb calc.py
今度は、"step "コマンドの代わりに "next "コマンドを使って、プログラム全体を再実行しなさい。そのときの入出力トレースも示しておきます。
/Users/junaid/Desktop/calc.py(1)
– operators = [ ‘+’, ‘-‘, ‘*’, ‘/’ ]
(Pdb) step
/Users/junaid/Desktop/calc.py(2)
– numbers = [ 10, 20 ]
.
.
.
.
/Users/junaid/Desktop/calc.py(6)calculator()
– print(“Operators available: ” )
(Pdb) step
Operators available:
/Users/junaid/Desktop/calc.py(8)calculator()
– for op in operators:
(Pdb) step
/Users/junaid/Desktop/calc.py(10)calculator()
– print(op)
(Pdb) step
+
/Users/junaid/Desktop/calc.py(8)calculator()
– for op in operators:
(Pdb) step
/Users/junaid/Desktop/calc.py(10)calculator()
– print(op)
.
.
.
.
“`
さて、両方の関数の出力トレースができましたので、どのように違うか見てみましょう。step関数では、
calculator` 関数が呼ばれると、その関数の中を移動して、「ステップ」で反復して、各ステップで何が起こっているかを正確に示していることがわかります。
しかし、next
関数の出力トレースを見ると、「main」関数が呼ばれたときに、その関数の内部で何が起こっているか(つまり、電卓関数のその後の呼び出し)を示さず、直接、一行/ステップで最終結果を出力していることがわかります。
これらのコマンドは、プログラムを繰り返し実行する際に、特定の関数をステップ実行したいが、他の関数をステップ実行したくない場合に、それぞれのコマンドを目的に応じて活用すると便利です。
結論
このチュートリアルでは、PDB という組み込みモジュールを使って Python アプリケーションをデバッグするための洗練されたテクニックを学びました。PDB が提供する next
や step
ステートメント、ブレークポイントなどの様々なトラブルシューティングコマンドについて掘り下げていきました。また、それらを基本的なプログラムに適用して、その動作を確認しました。
</module