Python(+ pyenv) | 3.11.5 |
---|
print()
関数やpprint
モジュールを使うことで、変数や式の値を表示できます。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の標準ライブラリとして提供されているデバッガーです。インタラクティブなデバッグ機能を提供しています。print()
関数でも事足ります。print()
を挟み込むことで、その箇所で期待通りの値が代入されているかを確認できます。しかしながら、print()
関数はデプロイ時に放置されてしまったり、思ったとおり動かないときに再実行が必要だったりと、使い勝手が最高と言いづらい難点もあります。pdb
です。pdb
は標準ライブラリのため、わざわざインストールする必要がありません。breakpoint()
を使うか、pdb
をimportして使います。import pdb
def say_hello(name: str):
return f'Hello {name}!'
name = 'Taro'
pdb.run("print(f'Say hello to {name}: {say_hello(name)}')")
import pdb; pdb.set_trace()
def say_hello(name: str):
return f'Hello {name}!'
name = 'Taro'
print(f'Say hello to {name}: {say_hello(name)}')
breakpoint()
def say_hello(name: str):
return f'Hello {name}!'
name = 'Taro'
print(f'Say hello to {name}: {say_hello(name)}')
$ python -m pdb xxx.py
pdb
のデバッガー画面が表示されます。簡単に実行を確認するため、ここではcontinue
を意味するc
を実行してみましょう。> myscript.py(3)<module>()
-> def say_hello(name: str):
(Pdb) c
Say hello to Taro: Hello Taro!
pdb
には、多くのコマンドが存在いますが、頻繁に使用するコマンドはその一部です。pdb
でデバッグすることに慣れましょう。list
(l
)pdb
のlist
コマンド(ショートコマンドはl
)は、現在の行の周りのソースコードを表示するために使われます。開発者がデバッグ中に自分のコードが実行されているコンテキストを理解するのに役立ちます。list
を利用する例です。def add(a, b):
result = a + b
return result
def main():
x = 5
y = 7
breakpoint() # ここにブレイクポイント
sum_value = add(x, y)
print(sum_value)
if __name__ == "__main__":
main()
$ python sample.py
list
、またはl
を実行すると以下のように表示されます。-> sum_value = add(x, y)
(Pdb) l
4
5 def main():
6 x = 5
7 y = 7
8 breakpoint()
9 -> sum_value = add(x, y)
10 print(sum_value)
11
12 if __name__ == "__main__":
13 main()
[EOF]
->
が表示されている行が現在実行している行です。list
は、現在実行している行の前後数行を表示します。next
(n
)pdb
のnext
コマンド(ショートコマンドはn
)は、現在の関数の次の行に到達するか、リターンするまで実行を続けるために使われます。next
コマンドはコードを一行ずつステップ実行できますが、関数に入ることはありません。(step
コマンドとの違い)next
コマンドの例は以下です。def add(a, b):
result = a + b
return result
def main():
x = 5
y = 7
breakpoint()
sum_value = add(x, y)
print(sum_value)
if __name__ == "__main__":
main()
$ python sample.py
l
コマンドで現在行を確認しましょう。(Pdb) l
4
5 def main():
6 x = 5
7 y = 7
8 breakpoint()
9 -> sum_value = add(x, y)
10 print(sum_value)
11
12 if __name__ == "__main__":
13 main()
[EOF]
next
かn
をタイプして実行します。デバッガーはsum_value = add(x, y)
の行に移動しますが、まだ実行はしません。(Pdb) n
> sample.py(10)main()
-> print(sum_value)
next
、またはn
をもう一度入力すると、デバッガはsum_value = add(x, y)
の行を実行します。(add関数には入らず実行されるだけ)。(Pdb) n
12
--Return--
> sample.py(10)main()->None
-> print(sum_value)
next
コマンドを使えばadd
関数内を探索することなくコードをステップ実行できます。もしnext
の代わりにstep
コマンドを使った場合、add
関数の中に入って、一行ずつデバッグしていくことになります。step
(s
)pdb
のstep
コマンド(ショートコマンドs
)を使うと、関数やメソッドに「入り込んで」その内部実行を調査できます。この特徴はnext
コマンドとは対照的です。step
コマンドを説明します。def add(a, b):
result = a + b
return result
def main():
x = 5
y = 7
breakpoint()
sum_value = add(x, y)
print(sum_value)
if __name__ == "__main__":
main()
$ python sample.py
step
コマンドを実行してみましょう。(Pdb) st
--Call--
> sample.py(1)add()
-> def add(a, b):
add()
関数内に入りました。再実行しましょう。(Pdb) s
> sample.py(2)add()
-> result = a + b
(Pdb) s
> sample.py(3)add()
-> return result
(Pdb) s
--Return--
> sample.py(3)add()->12
-> return result
step
コマンドを使うことで、関数やメソッドの内部を綿密に調べることができ、コードの各部の流れや動作を確実に理解できるようになります。continue
(c
)pdb
のcontinue
コマンド(ショートコマンドc
)を使うと、次のブレイクポイントにぶつかるまでプログラムの実行を再開できます。追加のブレークポイントが設定されていなければ、プログラムは完了するまで実行されます。continue
コマンドを説明します。def add(a, b):
result = a + b
return result
def main():
x = 5
y = 7
breakpoint() # 1つ目のブレイクポイント
sum_value = add(x, y)
print("Sum:", sum_value)
breakpoint() # 2つ目のブレイクポイント
print("Done.")
if __name__ == "__main__":
main()
$ python sample.py
> sample.py(9)main()
-> sum_value = add(x, y)
continue
またはc
を実行すると、プログラムは次のブレイクポイントに達するまで実行されます。ここではprint
文の後に2つ目のブレイクポイントを置いたので、デバッガーはそこで停止します。(Pdb) c
Sum: 12
> sample.py(12)main()
-> print("Done.")
continue
コマンドを実行すれば、残りのブレイクポイントがないため完了まで実行されます。(Pdb) c
Done.
continue
コマンドは、スクリプト内に複数のブレイクポイントがあり、その間にあるコードを1行ずつステップ実行することなく素早く移動したい場合に便利です。break
(b
)pdb
のbreak
コマンド(ショートコマンドb
)を使うと、特定の行や関数にブレイクポイントを設置できます。設置したブレイクポイントに達すると実行が一時停止され、その時点から変数の検査や式の評価、コードのステップスルーができるようになります。step
コマンドの例は以下です。def add(a, b):
result = a + b
return result
def subtract(a, b):
difference = a - b
return difference
def main():
x = 5
y = 7
breakpoint()
sum_value = add(x, y)
print("Sum:", sum_value)
difference_value = subtract(x, y)
print("Difference:", difference_value)
if __name__ == "__main__":
main()
$ python sample.py
break
コマンドでブレイクポイントを設置してみましょう。(Pdb) break subtract
Breakpoint 1 at sample.py:5
continue
またはc
を実行すると、プログラムはsubtract
関数にぶつかるまで実行されます。(Pdb) c
Sum: 12
> sample.py(6)subtract()
-> difference = a - b
(Pdb) break 16
Breakpoint 2 at sample.py:16
(Pdb) c
> sample.py(16)main()
-> print("Difference:", difference_value)
break
コマンドに引数を渡さなければ、設定したブレイクポイントを一覧表示できます。(Pdb) break
Num Type Disp Enb Where
1 breakpoint keep yes at sample.py:5
breakpoint already hit 1 time
2 breakpoint keep yes at sample.py:16
breakpoint already hit 1 time
break
コマンドを使えば、一時停止する箇所を効果的にコントロールでき、特定の箇所でコードの状態や振る舞いを調べられます。quit
(q
)quit
(ショートコマンドq
)を入力すれば終了します。(Pdb) q
~中略~
bdb.BdbQuit
help
(h
)help
コマンド(ショートコマンドh
)を使いましょう。(Pdb) h
Documented commands (type help <topic>):
========================================
EOF c d h list q rv undisplay
a cl debug help ll quit s unt
alias clear disable ignore longlist r source until
args commands display interact n restart step up
b condition down j next return tbreak w
break cont enable jump p retval u whatis
bt continue exit l pp run unalias where
Miscellaneous help topics:
==========================
exec pdb
help
コマンドに引数として渡しましょう。(Pdb) h where
w(here)
Print a stack trace, with the most recent frame at the bottom.
An arrow indicates the "current frame", which determines the
context of most commands. 'bt' is an alias for this command.
pdb
にはより高度なデバッグ機能があります。デバッグに高機能さを求めるケースは、頻繁ではありませんが、特に障害発生時の原因調査で有効です。pdb
では、特定の条件が満たされた時のみ実行される条件付きブレークポイントを設定できます。大きなデータをループ処理していて、特定の状況下でのみ実行を一時停止させたい場合に便利です。def process_numbers(numbers):
for num in numbers:
if num > 50:
print(f"High value: {num}")
else:
print(f"Regular value: {num}")
def main():
nums = [10, 25, 60, 45, 80]
breakpoint()
process_numbers(nums)
if __name__ == "__main__":
main()
$ python condition_sample.py
process_numbers(nums)
の行で停止します。process_numbers
関数の内部、if num > 50
の行に条件付きブレイクポイントを設定しましょう。(Pdb) b 4, num > 50
Breakpoint 1 at condition_sample.py:4
c
で実行を続行しましょう。すると、下記のようにnum
が50を越えるときに停止します。(Pdb) c
Regular value: 10
Regular value: 25
> condition_sample.py(4)process_numbers()
-> print(f"High value: {num}")
c
を実行し、再開しましょう。(Pdb) c
High value: 60
Regular value: 45
> condition_sample.py(4)process_numbers()
-> print(f"High value: {num}")
80
があるためもう1度停止します。post_mortem
post_mortem
関数を使うと、例外が発生した後にその例外を調べられます。特に、予期せぬ例外が発生した際、何が問題だったのかを理解するのに便利です。post_mortem
の使用例を示します。import pdb
def divide_numbers(a, b):
return a / b
def main():
x = 5
y = 0
try:
result = divide_numbers(x, y)
print(result)
except Exception as e:
print(e)
pdb.post_mortem()
if __name__ == "__main__":
main()
$ python error_sample.py
division by zero
> error_sample.py(5)divide_numbers()
-> return a / b
divide_numbers
関数の内部、ちょうど例外が発生した行に位置するところから開始されます。他のpdb
コマンドを使って、変数の検査、式の評価、コード内のナビゲーションをしてデバッグすることとなります。post_mortem
関数は、コード中に予期せぬ例外が発生し、例外が発生した瞬間のプログラムの状態を検査したい場合に有用です。jump
(j
)jump
コマンド(ショートコマンドj
)を使うと、次に実行する行を設定できます。コードの特定の部分をスキップしたり、前のポイントに戻って再実行したい場合に便利です。jump
コマンドにはいくつか注意すべき点があります。jump
コマンドを使ってみましょう。def simple_function():
print("Start of function")
breakpoint()
print("Middle of function")
print("End of function")
if __name__ == "__main__":
simple_function()
$ python jump_sample.py
print
文をスキップして、「関数の終わり」の行に直接ジャンプしたいとしましょう。目的の行番号を指定してジャンプできます。(Pdb) jump 5
> jump_sample.py(5)simple_function()
-> print("End of function")
c
を実行すると、最後のprint
実行が確認できます。(Pdb) c
End of function
(Pdb) jump 6
*** Jump failed: line 6 comes after the current code block
pp
pp
コマンドは"pretty print"の略です。辞書やリストのようなデータ構造を、より読みやすく整形して表示するのに役立ちます。pp
コマンドを紹介ます。def display_data():
data = {
"name": "Hanako",
"age": 30,
"address": {
"pref": "Fukuoka",
"zip": "810-0001"
},
"hobbies": ["reading", "gardening", "shogi"]
}
breakpoint()
print("Done.")
if __name__ == "__main__":
display_data()
$ python pp_sample.py
data
の内容を調査したい場合は、pp
コマンドを使うことできれいに表示できます。(Pdb) pp data
{'address': {'pref': 'Fukuoka', 'zip': '810-0001'},
'age': 30,
'hobbies': ['reading', 'gardening', 'shogi'],
'name': 'Hanako'}
print
であるp
コマンドを使った場合は以下のようになります。pp
のほうが見やすいことは一目瞭然です。(Pdb) p data
{'name': 'Hanako', 'age': 30, 'address': {'pref': 'Fukuoka', 'zip': '810-0001'}, 'hobbies': ['reading', 'gardening', 'shogi']}
pp
コマンドは、大きくネストされたデータ構造を扱う場合に有用となります。print
関数を使えばよいのですが、構造化されたデータの場合は読みやすさの点からpp
の方が望ましいでしょう。pdb
について紹介してきましたが、詳細なデバッグをするプログラミングは比較的大規模なものとなります。pdb
は標準ライブラリということもあり、他のライブラリと統合されていることもあるので、pdb
の使い方は覚えておいて損ありません。この機会にたくさん触って、手持ちスキルにしてしまいましょう。