Python(+ pyenv) 3.11.5
Pythonだけでなく、プログラミングをしている際に必ずする作業がデバッグです。Pythonであればprint()
関数やpprint
モジュールを使うことで、変数や式の値を表示できます。
ただし、この方法だと毎回実行しなくてはいけません。
そこで、今回はPythonにデフォルトで組み込まれているデバッガーpdb
をご紹介します。
ブレイクポイントと呼ばれる「止めるポイント」を設置することによって、段階的なコード内探索が可能になります。
pdb
のコマンドはあまり使うことがないため、先に一覧としてまとめておきます。
コマンド | ショートコマンド | 動作 |
---|---|---|
args | a | 引数を表示 |
break | b | 行数指定でブレイクポイントを置く |
clear | cl | ブレイクポイントの削除 |
continue | c, cont | 実行を続行する |
enable | - | 指定のブレイクポイントを有効化 |
disable | - | 指定のブレイクポイントを無効化 |
list | l | 現在行前後のソースコード表示 |
next | n | 関数の次の行に至るかreturnされるまで実行 |
down | d | 階層を下げる |
up | u | 階層を上げる |
step | s | 現在行を実行 |
where | w | 現在の行を表示 |
pp | - | 式の値を表示(= pretty-print ) |
restart | - | 現在デバッグ中のプログラムを再実行 |
return | r | returnされるまで実行 |
quit | q | デバッガの終了 |
help | h | コマンド一覧の表示 |
alias | - | エイリアスの作成 |
bt | - | whereのエイリアス |
commands | - | 指定のブレイクポイント番号にコマンドのリストを指定 |
condition | - | 指定のブレイクポイント番号に条件式を追加 |
debug | - | コードをステップ実行する再帰的デバッガに入る |
display | - | 式の変更を表示 |
exit | q | デバッガの終了 |
ignore | - | 指定数、ブレイクポイントを通過 |
interact | - | インタプリタを起動 |
jump | j | 次に実行する行の指定 |
longlist | ll | 現在の関数、またはフレームのソースコード表示 |
p | - | 式の値を表示 |
retval | rv | 最後にreturnされた値を表示 |
run | - | 現在デバッグ中のプログラムを再実行 |
source | - | 式のソースコード取得 |
tbreak | - | 一度で破棄されるブレイクポイントを設置 |
unalias | - | エイリアスの削除 |
undisplay | - | 現在のフレームで指定の式を表示しない |
until | unt | 現在行、または指定行より先になるまで実行 |
whatis | - | 型を表示 |
pdb
は、Pythonの標準ライブラリとして提供されているデバッガーです。インタラクティブなデバッグ機能を提供しています。
主な機能は以下です。
Pythonに限らず、プログラミングしているときには常にデバッグが必要となります。自身のコードが正しく機能していることを確認しながらでないと、思わぬバグを引き起こす可能性があります。
冒頭でも説明したように、単純にprint()
関数でも事足ります。
任意の箇所にprint()
を挟み込むことで、その箇所で期待通りの値が代入されているかを確認できます。しかしながら、print()
関数はデプロイ時に放置されてしまったり、思ったとおり動かないときに再実行が必要だったりと、使い勝手が最高と言いづらい難点もあります。
これを効率的に支えてくれるのがpdb
です。
pdb
は標準ライブラリのため、わざわざインストールする必要がありません。
単純にbreakpoint()
を使うか、pdb
をimportして使います。
スクリプト内で起動したいときは以下のように書きます。
または、先頭からデバッグを開始する場合、以下のコマンドで実行できます。
このようにすることで、pdb
のデバッガー画面が表示されます。簡単に実行を確認するため、ここではcontinue
を意味するc
を実行してみましょう。
pdb
には、多くのコマンドが存在いますが、頻繁に使用するコマンドはその一部です。
ここでは基本的なコマンドを覚えて、まずはpdb
でデバッグすることに慣れましょう。
list
(l
)pdb
のlist
コマンド(ショートコマンドはl
)は、現在の行の周りのソースコードを表示するために使われます。開発者がデバッグ中に自分のコードが実行されているコンテキストを理解するのに役立ちます。
以下、list
を利用する例です。
まずは簡単なコードを書いてみます。
開始されたデバッガーのプロンプトで、list
、またはl
を実行すると以下のように表示されます。
->
が表示されている行が現在実行している行です。list
は、現在実行している行の前後数行を表示します。
next
(n
)pdb
のnext
コマンド(ショートコマンドはn
)は、現在の関数の次の行に到達するか、リターンするまで実行を続けるために使われます。
基本的にnext
コマンドはコードを一行ずつステップ実行できますが、関数に入ることはありません。(step
コマンドとの違い)
next
コマンドの例は以下です。
まずはl
コマンドで現在行を確認しましょう。
9行目にいることがわかります。
次に、next
かn
をタイプして実行します。デバッガーはsum_value = add(x, y)
の行に移動しますが、まだ実行はしません。
next
、またはn
をもう一度入力すると、デバッガはsum_value = add(x, y)
の行を実行します。(add関数には入らず実行されるだけ)。
このように、next
コマンドを使えばadd
関数内を探索することなくコードをステップ実行できます。もしnext
の代わりにstep
コマンドを使った場合、add
関数の中に入って、一行ずつデバッグしていくことになります。
step
(s
)pdb
のstep
コマンド(ショートコマンドs
)を使うと、関数やメソッドに「入り込んで」その内部実行を調査できます。この特徴はnext
コマンドとは対照的です。
前の例を流用してstep
コマンドを説明します。
早速デバッガープロンプトでstep
コマンドを実行してみましょう。
まずはadd()
関数内に入りました。再実行しましょう。
さらに実行を進めます。
このようにstep
コマンドを使うことで、関数やメソッドの内部を綿密に調べることができ、コードの各部の流れや動作を確実に理解できるようになります。
continue
(c
)pdb
のcontinue
コマンド(ショートコマンドc
)を使うと、次のブレイクポイントにぶつかるまでプログラムの実行を再開できます。追加のブレークポイントが設定されていなければ、プログラムは完了するまで実行されます。
先ほどのコードを少し修正してcontinue
コマンドを説明します。
早速デバッガーに入ってみましょう。
まず、最初のブレイクポイントでに位置していることがわかります。
この時点でcontinue
またはc
を実行すると、プログラムは次のブレイクポイントに達するまで実行されます。ここではprint
文の後に2つ目のブレイクポイントを置いたので、デバッガーはそこで停止します。
ここでもう一度continue
コマンドを実行すれば、残りのブレイクポイントがないため完了まで実行されます。
continue
コマンドは、スクリプト内に複数のブレイクポイントがあり、その間にあるコードを1行ずつステップ実行することなく素早く移動したい場合に便利です。
break
(b
)pdb
のbreak
コマンド(ショートコマンドb
)を使うと、特定の行や関数にブレイクポイントを設置できます。設置したブレイクポイントに達すると実行が一時停止され、その時点から変数の検査や式の評価、コードのステップスルーができるようになります。
step
コマンドの例は以下です。
デバッガーに入ったら、break
コマンドでブレイクポイントを設置してみましょう。
ここでcontinue
またはc
を実行すると、プログラムはsubtract
関数にぶつかるまで実行されます。
また、行番号でブレイクポイントを設定可能です。たとえば、16行目(差分が出力される行)にブレイクポイントを設定したい場合は次のように入力します。
break
コマンドに引数を渡さなければ、設定したブレイクポイントを一覧表示できます。
break
コマンドを使えば、一時停止する箇所を効果的にコントロールでき、特定の箇所でコードの状態や振る舞いを調べられます。
quit
(q
)デバッガーを終了したい場合はquit
(ショートコマンドq
)を入力すれば終了します。
help
(h
)他のコマンドを調べたいときはhelp
コマンド(ショートコマンドh
)を使いましょう。
引数を渡さなければ一覧表示されます。
各コマンドを調べるときは、help
コマンドに引数として渡しましょう。
先述した基本的な機能とは別に、pdb
にはより高度なデバッグ機能があります。デバッグに高機能さを求めるケースは、頻繁ではありませんが、特に障害発生時の原因調査で有効です。
エンジニアになってから数年が経ち、アプリケーションだけでなくインフラやネットワークなど広範囲で世話するようになると、障害発生時の原因調査は大切なタスクとして降り掛かってきます。
そういったときでも慌てないように、プログラムを追う方法を見ていきましょう。
pdb
では、特定の条件が満たされた時のみ実行される条件付きブレークポイントを設定できます。大きなデータをループ処理していて、特定の状況下でのみ実行を一時停止させたい場合に便利です。
例えば、数値のリストを処理するループがあり、特定の条件(例えば、ある閾値より大きな数値)に遭遇して停止させたい場合を考えてみましょう。
今回の例では以下のスクリプトを使います。
上記コマンドでデバッガーに入ると、process_numbers(nums)
の行で停止します。
ここで、50以上の数値が処理されたときに実行を一時停止したいとします。process_numbers
関数の内部、if num > 50
の行に条件付きブレイクポイントを設定しましょう。
c
で実行を続行しましょう。すると、下記のようにnum
が50を越えるときに停止します。
もう一度c
を実行し、再開しましょう。
上記のように、以後の処理が走りますが、最後に80
があるためもう1度停止します。
post_mortem
post_mortem
関数を使うと、例外が発生した後にその例外を調べられます。特に、予期せぬ例外が発生した際、何が問題だったのかを理解するのに便利です。
以下にpost_mortem
の使用例を示します。
このスクリプトを実行してみましょう。
以下のように、例外が発生し、エラー内容と共にデバッガーが起動します。
この場合、divide_numbers
関数の内部、ちょうど例外が発生した行に位置するところから開始されます。他のpdb
コマンドを使って、変数の検査、式の評価、コード内のナビゲーションをしてデバッグすることとなります。
post_mortem
関数は、コード中に予期せぬ例外が発生し、例外が発生した瞬間のプログラムの状態を検査したい場合に有用です。
jump
(j
)jump
コマンド(ショートコマンドj
)を使うと、次に実行する行を設定できます。コードの特定の部分をスキップしたり、前のポイントに戻って再実行したい場合に便利です。
ただし、jump
コマンドにはいくつか注意すべき点があります。
これらの注意点を念頭に置いて、実際にjump
コマンドを使ってみましょう。
デバッガーに入って始めてください。
例えば、「関数の途中」のprint
文をスキップして、「関数の終わり」の行に直接ジャンプしたいとしましょう。目的の行番号を指定してジャンプできます。
ここでc
を実行すると、最後のprint
実行が確認できます。
これがもし、関数外などジャンプ制限に引っかかった場合は、以下のようにエラーが出ます。
pp
pp
コマンドは"pretty print"の略です。辞書やリストのようなデータ構造を、より読みやすく整形して表示するのに役立ちます。
例を使ってpp
コマンドを紹介ます。
変数data
の内容を調査したい場合は、pp
コマンドを使うことできれいに表示できます。
print
であるp
コマンドを使った場合は以下のようになります。pp
のほうが見やすいことは一目瞭然です。
pp
コマンドは、大きくネストされたデータ構造を扱う場合に有用となります。
単純に変数の中身を見るにはprint
関数を使えばよいのですが、構造化されたデータの場合は読みやすさの点からpp
の方が望ましいでしょう。
ここまで、pdb
について紹介してきましたが、詳細なデバッグをするプログラミングは比較的大規模なものとなります。
デバッグだけに頼らず、単体テストやlinterを駆使して高品質なコードを書けるようになることが大切です。
pdb
は標準ライブラリということもあり、他のライブラリと統合されていることもあるので、pdb
の使い方は覚えておいて損ありません。この機会にたくさん触って、手持ちスキルにしてしまいましょう。