pdb
--- Python 偵錯器¶
原始碼:Lib/pdb.py
pdb
模組定義了一個 Python 程式的互動式原始碼偵錯器。它支援在原始碼列層級 (source line level) 設定(條件式的)斷點 (breakpoint) 和單步執行、檢視 stack frame(堆疊框)、列出原始碼、以及在任何 stack frame 情境 (context) 中為任意 Python 程式碼求值 (evaluation)。它還支援事後偵錯 (post-mortem debugging),並可以在程式控制下呼叫。
偵錯器是可擴充的 —— 偵錯器實際被定義為 Pdb
類別。該類別目前沒有文件,但可以很容易地透過閱讀原始碼來理解它。擴充套件介面使用了 bdb
和 cmd
模組。
也參考
faulthandler
模組用於在出現故障時、超時 (timeout) 後或於接收到使用者訊號時,顯式地轉儲 (dump) Python 回溯 (traceback)。
traceback
模組用於提取、格式化和印出 Python 程式 stack trace(堆疊追蹤)的標準介面。
自一個執行中程式切入偵錯器的典型用法為插入:
import pdb; pdb.set_trace()
或:
breakpoint()
到你想切入偵錯器的位置,然後執行程式,就可以單步執行上述陳述式之後的程式碼,並可以使用 continue
命令來離開偵錯器、繼續執行。
在 3.7 版的變更: 當使用預設值呼叫時,可以使用內建的 breakpoint()
來取代 import pdb; pdb.set_trace()
。
def double(x):
breakpoint()
return x * 2
val = 3
print(f"{val} * 2 is {double(val)}")
偵錯器的提示字元是 (Pdb)
,這表示你處於偵錯模式:
> ...(2)double()
-> breakpoint()
(Pdb) p x
3
(Pdb) continue
3 * 2 is 6
在 3.3 版的變更: 透過 readline
模組達成的 tab 補全可用於補全本模組的命令和命令的引數,例如會提供當前的全域和區域名稱以作為 p
命令的引數。
你還可以從命令列調用 pdb
來偵錯其他腳本。例如:
python -m pdb myscript.py
當作為模組調用時,如果被偵錯的程序不正常地退出,pdb 將自動進入事後偵錯。事後偵錯後(或程式正常退出後),pdb 將重新啟動程式。自動重新啟動會保留 pdb 的狀態(例如斷點),並且在大多數情況下比在程式退出時退出偵錯器更有用。
在 3.2 版的變更: 新增了 -c
選項來執行命令,就像能在 .pdbrc
檔案中給定的那樣;請參閱偵錯器命令。
在 3.7 版的變更: 新增了 -m
選項以類似於 python -m
的方式來執行模組。與腳本一樣,偵錯器將在模組的第一列之前暫停執行。
在偵錯器控制下執行陳述式的典型用法是:
>>> import pdb
>>> def f(x):
... print(1 / x)
>>> pdb.run("f(2)")
> <string>(1)<module>()
(Pdb) continue
0.5
>>>
檢查一個損壞程式的典型用法:
>>> import pdb
>>> def f(x):
... print(1 / x)
...
>>> f(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
ZeroDivisionError: division by zero
>>> pdb.pm()
> <stdin>(2)f()
(Pdb) p x
0
(Pdb)
本模組定義了下列函式,每個函式進入偵錯器的方式略有不同:
- pdb.run(statement, globals=None, locals=None)¶
在偵錯器控制下執行 statement(以字串或程式碼物件形式給定)。偵錯提示字元會在執行任何程式碼前出現;你可以設定斷點並輸入
continue
,或也可以使用step
或next
逐步執行陳述式(這些命令在下面都有說明)。可選引數 globals 和 locals 指定程式碼執行的環境;預設使用__main__
模組的字典。(請參閱內建函式exec()
或eval()
的說明。)
- pdb.runeval(expression, globals=None, locals=None)¶
在偵錯器控制下為 expression 求值(以字串或程式碼物件形式給定)。當
runeval()
回傳時,它回傳 expression 的值。除此之外,該函式與run()
類似。
- pdb.runcall(function, *args, **kwds)¶
使用給定的引數呼叫 function(只可以是函式或方法物件,不能是字串)。
runcall()
回傳的是所呼叫函式的回傳值。偵錯器提示字元將在進入函式後立即出現。
- pdb.set_trace(*, header=None)¶
在呼叫此函式的 stack frame 進入偵錯器。用於在程式中給定之處寫死 (hard-code) 一個斷點,即便該程式碼不在偵錯狀態(如斷言失敗時)。如有給定 header,它將在偵錯正要開始前被印出到控制台。
在 3.7 版的變更: 僅限關鍵字引數 header。
在 3.13 版的變更:
set_trace()
將立即進入偵錯器,而不是在下一列要執行的程式碼中。
- pdb.post_mortem(traceback=None)¶
進入所給定 traceback 物件的事後偵錯。如果沒有給定 traceback,預設使用當前正在處理的例外之一(使用預設情況時,必須要有正在處理的例外存在)。
- pdb.pm()¶
進入在
sys.last_exc
中發現的例外的事後偵錯。
run*
函式和 set_trace()
都是別名,用於實例化 (instantiate) Pdb
類別並呼叫同名方法。如果要使用更多功能,則必須自己執行以下操作:
- class pdb.Pdb(completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True)¶
Pdb
是偵錯器類別。completekey、stdin 與 stdout 引數會被傳到底層的
cmd.Cmd
類別;請於該文件閱讀相關敘述。如果給定 skip 引數,則它必須是一個給出 glob 樣式之模組名稱的疊代器。如果遇到匹配這些樣式的模組,偵錯器將不會進入來自該模組的 frame。 [1]
預設情況下,當你發出
continue
命令時,Pdb 會為 SIGINT 訊號(即使用者在控制台上按下 Ctrl-C 時會發送的訊號)設定一個處理程式 (handler),這允許你透過按下 Ctrl-C 再次切入偵錯器。如果你希望 Pdb 不影響到 SIGINT 處理程式,請將 nosigint 設定為 true。readrc 引數預設為 true,它控制 Pdb 是否從檔案系統載入 .pdbrc 檔案。
啟用追蹤 (tracing) 且帶有 skip 引數的呼叫示範:
import pdb; pdb.Pdb(skip=['django.*']).set_trace()
不帶引數地引發一個稽核事件 (auditing event)
pdb.Pdb
。在 3.1 版的變更: 新增了 skip 參數。
在 3.2 版的變更: 新增了 nosigint 參數。以前 SIGINT 處理程式從未被 Pdb 設定過。
在 3.6 版的變更: readrc 引數。
偵錯器命令¶
下方列出的是偵錯器能認得的命令。如下所示,大多數命令可以縮寫為一個或兩個字母。如 h(elp)
表示可以輸入 h
或 help
來輸入幫助命令(但不能輸入 he
或 hel
,也不能是 H
或 Help
或 HELP
)。命令的引數必須用空格(空格符 (spaces) 或製表符 (tabs))分隔。在命令語法中,可選引數被括在方括號 ([]
) 中;使用時請勿輸入方括號。命令語法中的選擇項由豎線 (|
) 分隔。
輸入一個空白列 (blank line) 將重複上次輸入的命令。例外:如果上一個命令是 list
命令,則會列出接下來的 11 列。
偵錯器無法識別的命令將被認為是 Python 陳述式,並以正在偵錯的程式之情境來執行。Python 陳述式也可以用驚嘆號 (!
) 作為前綴,這是檢視正在偵錯之程式的強大方法,甚至可以修改變數或呼叫函式。當此類陳述式發生例外,將印出例外名稱,但偵錯器的狀態不會改變。
在 3.13 版的變更: 現在可以正確辨識並執行前綴為 pdb 命令的運算式/陳述式。
偵錯器有支援設定別名。別名可以有參數,使得偵錯器對被檢查的情境有一定程度的適應性。
在一列中可以輸入多個以 ;;
分隔的命令。(不能使用單個 ;
,因為它用於分隔傳遞給 Python 剖析器一列中的多個命令。)切分命令沒運用什麼高深的方式;輸入總是在第一處 ;;
被切分開,即使它位於引號內的字串之中。對於具有雙分號字串的一個變通解法,是使用隱式字串連接 ';'';'
或 ";"";"
。
要設定臨時全域變數,請使用便利變數 (convenience variable)。便利變數是名稱以 $
開頭的變數。例如 $foo = 1
會設定一個全域變數 $foo
,你可以在偵錯器會話 (debugger session) 中使用它。當程式恢復執行時,便利變數將被清除,因此與使用 foo = 1
等普通變數相比,它不太會去干擾你的程式。
共有三個預先設定的便利變數:
$_frame
:當前正在偵錯的 frame$_retval
:frame 回傳時的回傳值$_exception
:frame 引發例外時的例外
在 3.12 版被加入: 新增了便利變數功能。
如果 .pdbrc
檔案存在於使用者的家目錄或當前目錄中,則會使用 'utf-8'
編碼讀取並執行該檔案,就像在偵錯器提示字元下鍵入該檔案一樣,除了空列和以 #
開頭的列會被忽略之外。這對於別名設定特別有用。如果兩個檔案都存在,則先讀取家目錄中的檔案,且定義於其中的別名可以被本地檔案覆蓋。
在 3.11 版的變更: .pdbrc
現在使用 'utf-8'
編碼讀取。以前它是使用系統區域設定編碼讀取的。
- h(elp) [command]¶
如不帶引數,印出可用的命令列表。引數為 command 時,印出有關該命令的幫助訊息,
help pdb
會顯示完整文件(即pdb
模組的說明字串 (docstring))。由於 command 引數必須是一個識別字 (identifier),若要獲取!
命令的幫助訊息則必須輸入help exec
。
- w(here)¶
印出 stack trace,最新的 frame 會位於底部。箭頭(
>
)表示當前的 frame,它也決定了大多數命令的情境。
- d(own) [count]¶
在 stack trace 中,將當前 frame 向下移動 count 級(預設為 1 級,移往較新的 frame)。
- u(p) [count]¶
在 stack trace 中,將當前 frame 向上移動 count 級(預設為 1 級,移向較舊的 frame)。
- b(reak) [([filename:]lineno | function) [, condition]]¶
如帶有 lineno 引數,則在目前檔案中的 lineno 列處設定中斷。列號可以以 filename 和冒號為前綴,以指定另一個檔案(可能是尚未載入的檔案)中的斷點。該檔案會在
sys.path
上搜尋。可接受的 filename 形式為/abspath/to/file.py
、relpath/file.py
、module
和package.module
。如帶有 function 引數,在該函式內的第一個可執行陳述式處設定中斷。function 可以是任何其求值結果為目前命名空間中函式的運算式。
如果第二個引數存在,它是一個運算式,在斷點生效前其必須求值為 true
如果不帶引數執行會列出所有斷點資訊,包括每個斷點、命中該斷點的次數、當前的忽略次數以及關聯的條件(如存在)。
每個斷點都有賦予一個編號,所有其他斷點命令都參照該編號。
- cl(ear) [filename:lineno | bpnumber ...]¶
如帶有 filename:lineno 引數,則清除此列上的所有斷點。如果引數是空格分隔的斷點編號列表,則清除這些斷點。如果不帶引數則清除所有斷點(但會先提示確認)。
- disable bpnumber [bpnumber ...]¶
停用斷點,斷點以空格分隔的斷點編號列表來給定。停用斷點表示它不會導致程式停止執行,但是與清除斷點不同,停用的斷點將保留在斷點列表中並且可以(重新)啟用。
- enable bpnumber [bpnumber ...]¶
Enable the breakpoints specified.
- ignore bpnumber [count]¶
Set the ignore count for the given breakpoint number. If count is omitted, the ignore count is set to 0. A breakpoint becomes active when the ignore count is zero. When non-zero, the count is decremented each time the breakpoint is reached and the breakpoint is not disabled and any associated condition evaluates to true.
- condition bpnumber [condition]¶
Set a new condition for the breakpoint, an expression which must evaluate to true before the breakpoint is honored. If condition is absent, any existing condition is removed; i.e., the breakpoint is made unconditional.
- commands [bpnumber]¶
Specify a list of commands for breakpoint number bpnumber. The commands themselves appear on the following lines. Type a line containing just
end
to terminate the commands. An example:(Pdb) commands 1 (com) p some_variable (com) end (Pdb)
To remove all commands from a breakpoint, type
commands
and follow it immediately withend
; that is, give no commands.With no bpnumber argument,
commands
refers to the last breakpoint set.You can use breakpoint commands to start your program up again. Simply use the
continue
command, orstep
, or any other command that resumes execution.Specifying any command resuming execution (currently
continue
,step
,next
,return
,jump
,quit
and their abbreviations) terminates the command list (as if that command was immediately followed by end). This is because any time you resume execution (even with a simple next or step), you may encounter another breakpoint—which could have its own command list, leading to ambiguities about which list to execute.If you use the
silent
command in the command list, the usual message about stopping at a breakpoint is not printed. This may be desirable for breakpoints that are to print a specific message and then continue. If none of the other commands print anything, you see no sign that the breakpoint was reached.
- s(tep)¶
Execute the current line, stop at the first possible occasion (either in a function that is called or on the next line in the current function).
- n(ext)¶
Continue execution until the next line in the current function is reached or it returns. (The difference between
next
andstep
is thatstep
stops inside a called function, whilenext
executes called functions at (nearly) full speed, only stopping at the next line in the current function.)
- unt(il) [lineno]¶
Without argument, continue execution until the line with a number greater than the current one is reached.
With lineno, continue execution until a line with a number greater or equal to lineno is reached. In both cases, also stop when the current frame returns.
在 3.2 版的變更: Allow giving an explicit line number.
- r(eturn)¶
Continue execution until the current function returns.
- c(ont(inue))¶
Continue execution, only stop when a breakpoint is encountered.
- j(ump) lineno¶
Set the next line that will be executed. Only available in the bottom-most frame. This lets you jump back and execute code again, or jump forward to skip code that you don't want to run.
It should be noted that not all jumps are allowed -- for instance it is not possible to jump into the middle of a
for
loop or out of afinally
clause.
- l(ist) [first[, last]]¶
List source code for the current file. Without arguments, list 11 lines around the current line or continue the previous listing. With
.
as argument, list 11 lines around the current line. With one argument, list 11 lines around at that line. With two arguments, list the given range; if the second argument is less than the first, it is interpreted as a count.The current line in the current frame is indicated by
->
. If an exception is being debugged, the line where the exception was originally raised or propagated is indicated by>>
, if it differs from the current line.在 3.2 版的變更: Added the
>>
marker.
- ll | longlist¶
List all source code for the current function or frame. Interesting lines are marked as for
list
.在 3.2 版被加入.
- a(rgs)¶
Print the arguments of the current function and their current values.
- p expression¶
Evaluate expression in the current context and print its value.
備註
print()
can also be used, but is not a debugger command --- this executes the Pythonprint()
function.
- pp expression¶
Like the
p
command, except the value of expression is pretty-printed using thepprint
module.
- whatis expression¶
Print the type of expression.
- source expression¶
Try to get source code of expression and display it.
在 3.2 版被加入.
- display [expression]¶
Display the value of expression if it changed, each time execution stops in the current frame.
Without expression, list all display expressions for the current frame.
備註
Display evaluates expression and compares to the result of the previous evaluation of expression, so when the result is mutable, display may not be able to pick up the changes.
範例:
lst = [] breakpoint() pass lst.append(1) print(lst)
Display won't realize
lst
has been changed because the result of evaluation is modified in place bylst.append(1)
before being compared:> example.py(3)<module>() -> pass (Pdb) display lst display lst: [] (Pdb) n > example.py(4)<module>() -> lst.append(1) (Pdb) n > example.py(5)<module>() -> print(lst) (Pdb)
You can do some tricks with copy mechanism to make it work:
> example.py(3)<module>() -> pass (Pdb) display lst[:] display lst[:]: [] (Pdb) n > example.py(4)<module>() -> lst.append(1) (Pdb) n > example.py(5)<module>() -> print(lst) display lst[:]: [1] [old: []] (Pdb)
在 3.2 版被加入.
- undisplay [expression]¶
Do not display expression anymore in the current frame. Without expression, clear all display expressions for the current frame.
在 3.2 版被加入.
- interact¶
Start an interactive interpreter (using the
code
module) in a new global namespace initialised from the local and global namespaces for the current scope. Useexit()
orquit()
to exit the interpreter and return to the debugger.備註
As
interact
creates a new dedicated namespace for code execution, assignments to variables will not affect the original namespaces. However, modifications to any referenced mutable objects will be reflected in the original namespaces as usual.在 3.2 版被加入.
在 3.13 版的變更:
exit()
andquit()
can be used to exit theinteract
command.在 3.13 版的變更:
interact
directs its output to the debugger's output channel rather thansys.stderr
.
- alias [name [command]]¶
Create an alias called name that executes command. The command must not be enclosed in quotes. Replaceable parameters can be indicated by
%1
,%2
, ... and%9
, while%*
is replaced by all the parameters. If command is omitted, the current alias for name is shown. If no arguments are given, all aliases are listed.Aliases may be nested and can contain anything that can be legally typed at the pdb prompt. Note that internal pdb commands can be overridden by aliases. Such a command is then hidden until the alias is removed. Aliasing is recursively applied to the first word of the command line; all other words in the line are left alone.
As an example, here are two useful aliases (especially when placed in the
.pdbrc
file):# Print instance variables (usage "pi classInst") alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}") # Print instance variables in self alias ps pi self
- unalias name¶
Delete the specified alias name.
- ! statement¶
Execute the (one-line) statement in the context of the current stack frame. The exclamation point can be omitted unless the first word of the statement resembles a debugger command, e.g.:
(Pdb) ! n=42 (Pdb)
To set a global variable, you can prefix the assignment command with a
global
statement on the same line, e.g.:(Pdb) global list_options; list_options = ['-l'] (Pdb)
- run [args ...]¶
- restart [args ...]¶
Restart the debugged Python program. If args is supplied, it is split with
shlex
and the result is used as the newsys.argv
. History, breakpoints, actions and debugger options are preserved.restart
is an alias forrun
.
- q(uit)¶
Quit from the debugger. The program being executed is aborted.
- debug code¶
Enter a recursive debugger that steps through code (which is an arbitrary expression or statement to be executed in the current environment).
- retval¶
Print the return value for the last return of the current function.
- exceptions [excnumber]¶
List or jump between chained exceptions.
When using
pdb.pm()
orPdb.post_mortem(...)
with a chained exception instead of a traceback, it allows the user to move between the chained exceptions usingexceptions
command to list exceptions, andexception <number>
to switch to that exception.範例:
def out(): try: middle() except Exception as e: raise ValueError("reraise middle() error") from e def middle(): try: return inner(0) except Exception as e: raise ValueError("Middle fail") def inner(x): 1 / x out()
calling
pdb.pm()
will allow to move between exceptions:> example.py(5)out() -> raise ValueError("reraise middle() error") from e (Pdb) exceptions 0 ZeroDivisionError('division by zero') 1 ValueError('Middle fail') > 2 ValueError('reraise middle() error') (Pdb) exceptions 0 > example.py(16)inner() -> 1 / x (Pdb) up > example.py(10)middle() -> return inner(0)
在 3.13 版被加入.
註解