pdb --- Python 的調試器?

源代碼: Lib/pdb.py


pdb 模塊定義了一個(gè)交互式源代碼調試器,用于 Python 程序。它支持在源碼行間設置(有條件的)斷點(diǎn)和單步執行,檢視堆棧幀,列出源碼列表,以及在任何堆棧幀的上下文中運行任意 Python 代碼。它還支持事后調試,可以在程序控制下調用。

調試器是可擴展的——調試器實(shí)際被定義為 Pdb 類(lèi)。該類(lèi)目前沒(méi)有文檔,但通過(guò)閱讀源碼很容易理解它。擴展接口使用了 bdbcmd 模塊。

調試器的提示符是 (Pdb)。在調試器的控制下運行程序的典型用法是:

>>>
>>> import pdb
>>> import mymodule
>>> pdb.run('mymodule.test()')
> <string>(0)?()
(Pdb) continue
> <string>(1)?()
(Pdb) continue
NameError: 'spam'
> <string>(1)?()
(Pdb)

在 3.3 版更改: readline 模塊實(shí)現的 Tab 補全可用于補全本模塊的命令和命令的參數,例如,Tab 補全會(huì )提供當前的全局變量和局部變量,用作 p 命令的參數。

也可以將 pdb.py 作為腳本調用,來(lái)調試其他腳本。例如:

python3 -m pdb myscript.py

當作為腳本調用時(shí),如果要調試的程序異常退出,pdb 調試將自動(dòng)進(jìn)入事后調試。事后調試之后(或程序正常退出之后),pdb 將重新啟動(dòng)程序。自動(dòng)重啟會(huì )保留 pdb 的狀態(tài)(如斷點(diǎn)),在大多數情況下,這比在退出程序的同時(shí)退出調試器更加實(shí)用。

3.2 新版功能: pdb.py 現在接受 -c 選項,可以執行命令,這與將該命令寫(xiě)入 .pdbrc 文件相同,請參閱 調試器命令。

3.7 新版功能: pdb.py 現在接受 -m 選項,該選項用于執行一個(gè)模塊,類(lèi)似于 python3 -m。與腳本相同,調試器將暫停在待執行模塊第一行前。

The typical usage to break into the debugger is to insert:

import pdb; pdb.set_trace()

at the location you want to break into the debugger, and then run the program. You can then step through the code following this statement, and continue running without the debugger using the continue command.

3.7 新版功能: 內置函數 breakpoint(),當以默認參數調用它時(shí),可以用來(lái)代替 import pdb; pdb.set_trace()。

檢查已崩潰程序的典型用法是:

>>>
>>> import pdb
>>> import mymodule
>>> mymodule.test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./mymodule.py", line 4, in test
    test2()
  File "./mymodule.py", line 3, in test2
    print(spam)
NameError: spam
>>> pdb.pm()
> ./mymodule.py(3)test2()
-> print(spam)
(Pdb)

本模塊定義了下列函數,每個(gè)函數進(jìn)入調試器的方式略有不同:

pdb.run(statement, globals=None, locals=None)?

在調試器控制范圍內執行 statement (以字符串或代碼對象的形式提供)。調試器提示符會(huì )在執行代碼前出現,你可以設置斷點(diǎn)并鍵入 continue,也可以使用 stepnext 逐步執行語(yǔ)句(上述所有命令在后文有說(shuō)明)??蛇x參數 globalslocals 指定代碼執行環(huán)境,默認時(shí)使用 __main__ 模塊的字典。(請參閱內置函數 exec()eval() 的說(shuō)明。)

pdb.runeval(expression, globals=None, locals=None)?

在調試器控制范圍內執行 expression (以字符串或代碼對象的形式提供)。runeval() 返回時(shí)將返回表達式的值。本函數在其他方面與 run() 類(lèi)似。

pdb.runcall(function, *args, **kwds)?

使用給定的參數調用 function (以函數或方法對象的形式提供,不能是字符串)。runcall() 返回的是所調用函數的返回值。調試器提示符將在進(jìn)入函數后立即出現。

pdb.set_trace(*, header=None)?

在調用本函數的堆棧幀處進(jìn)入調試器。用于硬編碼一個(gè)斷點(diǎn)到程序中的固定點(diǎn)處,即使該代碼不在調試狀態(tài)(如斷言失敗時(shí))。如果傳入 header,它將在調試開(kāi)始前被打印到控制臺。

在 3.7 版更改: 僅關(guān)鍵字參數 header。

pdb.post_mortem(traceback=None)?

進(jìn)入 traceback 對象的事后調試。如果沒(méi)有給定 traceback,默認使用當前正在處理的異常之一(默認時(shí),必須存在正在處理的異常)。

pdb.pm()?

sys.last_traceback 中查找 traceback,并進(jìn)入其事后調試。

run* 函數和 set_trace() 都是別名,用于實(shí)例化 Pdb 類(lèi)和調用同名方法。如果要使用其他功能,則必須自己執行以下操作:

class pdb.Pdb(completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True)?

Pdb 是調試器類(lèi)。

completekey、stdinstdout 參數都會(huì )傳遞給底層的 cmd.Cmd 類(lèi),請參考相應的描述。

如果給出 skip 參數,則它必須是一個(gè)迭代器,可以迭代出 glob-style 樣式的模塊名稱(chēng)。如果遇到匹配上述樣式的模塊,調試器將不會(huì )進(jìn)入來(lái)自該模塊的堆棧幀。 1

默認情況下,當發(fā)出 continue 命令時(shí),Pdb 將為 SIGINT 信號設置一個(gè)處理程序(SIGINT 信號是用戶(hù)在控制臺按 Ctrl-C 時(shí)發(fā)出的)。這使用戶(hù)可以按 Ctrl-C 再次進(jìn)入調試器。如果希望 Pdb 不要修改 SIGINT 處理程序,請將 nosigint 設置為 true。

readrc 參數默認為 true,它控制 Pdb 是否從文件系統加載 .pdbrc 文件。

啟用跟蹤且帶有 skip 參數的調用示范:

import pdb; pdb.Pdb(skip=['django.*']).set_trace()

引發(fā)一個(gè) 審計事件 pdb.Pdb,沒(méi)有附帶參數。

3.1 新版功能: skip 參數。

3.2 新版功能: nosigint 參數。在這之前,Pdb 不為 SIGINT 設置處理程序。

在 3.6 版更改: readrc 參數。

run(statement, globals=None, locals=None)?
runeval(expression, globals=None, locals=None)?
runcall(function, *args, **kwds)?
set_trace()?

請參閱上文解釋同名函數的文檔。

調試器命令?

下方列出的是調試器可接受的命令。如下所示,大多數命令可以縮寫(xiě)為一個(gè)或兩個(gè)字母。如 h(elp) 表示可以輸入 hhelp 來(lái)輸入幫助命令(但不能輸入 hehel,也不能是 HHelpHELP)。命令的參數必須用空格(空格符或制表符)分隔。在命令語(yǔ)法中,可選參數括在方括號 ([]) 中,使用時(shí)請勿輸入方括號。命令語(yǔ)法中的選擇項由豎線(xiàn) (|) 分隔。

輸入一個(gè)空白行將重復最后輸入的命令。例外:如果最后一個(gè)命令是 list 命令,則會(huì )列出接下來(lái)的 11 行。

調試器無(wú)法識別的命令將被認為是 Python 語(yǔ)句,并在正在調試的程序的上下文中執行。Python 語(yǔ)句也可以用感嘆號 (!) 作為前綴。這是檢查正在調試的程序的強大方法,甚至可以修改變量或調用函數。當此類(lèi)語(yǔ)句發(fā)生異常,將打印異常名稱(chēng),但調試器的狀態(tài)不會(huì )改變。

調試器支持 別名。別名可以有參數,使得調試器對被檢查的上下文有一定程度的適應性。

Multiple commands may be entered on a single line, separated by ;;. (A single ; is not used as it is the separator for multiple commands in a line that is passed to the Python parser.) No intelligence is applied to separating the commands; the input is split at the first ;; pair, even if it is in the middle of a quoted string. A workaround for strings with double semicolons is to use implicit string concatenation ';'';' or ";"";".

If a file .pdbrc exists in the user's home directory or in the current directory, it is read with 'utf-8' encoding and executed as if it had been typed at the debugger prompt. This is particularly useful for aliases. If both files exist, the one in the home directory is read first and aliases defined there can be overridden by the local file.

在 3.11 版更改: .pdbrc is now read with 'utf-8' encoding. Previously, it was read with the system locale encoding.

在 3.2 版更改: .pdbrc 現在可以包含繼續調試的命令,如 continuenext。文件中的這些命令以前是無(wú)效的。

h(elp) [command]?

不帶參數時(shí),顯示可用的命令列表。參數為 command 時(shí),打印有關(guān)該命令的幫助。help pdb 顯示完整文檔(即 pdb 模塊的文檔字符串)。由于 command 參數必須是標識符,因此要獲取 ! 的幫助必須輸入 help exec。

w(here)?

打印堆?;厮?,最新一幀在底部。有一個(gè)箭頭指向當前幀,該幀決定了大多數命令的上下文。

d(own) [count]?

在堆?;厮葜?,將當前幀向下移動(dòng) count 級(默認為 1 級,移向更新的幀)。

u(p) [count]?

在堆?;厮葜?,將當前幀向上移動(dòng) count 級(默認為 1 級,移向更老的幀)。

b(reak) [([filename:]lineno | function) [, condition]]?

如果帶有 lineno 參數,則在當前文件相應行處設置一個(gè)斷點(diǎn)。如果帶有 function 參數,則在該函數的第一條可執行語(yǔ)句處設置一個(gè)斷點(diǎn)。行號可以加上文件名和冒號作為前綴,以在另一個(gè)文件(可能是尚未加載的文件)中設置一個(gè)斷點(diǎn)。另一個(gè)文件將在 sys.path 范圍內搜索。請注意,每個(gè)斷點(diǎn)都分配有一個(gè)編號,其他所有斷點(diǎn)命令都引用該編號。

如果第二個(gè)參數存在,它應該是一個(gè)表達式,且它的計算值為 true 時(shí)斷點(diǎn)才起作用。

如果不帶參數執行,將列出所有中斷,包括每個(gè)斷點(diǎn)、命中該斷點(diǎn)的次數、當前的忽略次數以及關(guān)聯(lián)的條件(如果有)。

tbreak [([filename:]lineno | function) [, condition]]?

臨時(shí)斷點(diǎn),在第一次命中時(shí)會(huì )自動(dòng)刪除。它的參數與 break 相同。

cl(ear) [filename:lineno | bpnumber ...]?

如果參數是 filename:lineno,則清除此行上的所有斷點(diǎn)。如果參數是空格分隔的斷點(diǎn)編號列表,則清除這些斷點(diǎn)。如果不帶參數,則清除所有斷點(diǎn)(但會(huì )先提示確認)。

disable [bpnumber ...]?

禁用斷點(diǎn),斷點(diǎn)以空格分隔的斷點(diǎn)編號列表給出。禁用斷點(diǎn)表示它不會(huì )導致程序停止執行,但是與清除斷點(diǎn)不同,禁用的斷點(diǎn)將保留在斷點(diǎn)列表中并且可以(重新)啟用。

enable [bpnumber ...]?

啟用指定的斷點(diǎn)。

ignore bpnumber [count]?

為指定的斷點(diǎn)編號設置忽略次數。如果省略 count,則忽略次數將設置為 0。忽略次數為 0 時(shí)斷點(diǎn)將變?yōu)榛顒?dòng)狀態(tài)。如果為非零值,在每次達到斷點(diǎn),且斷點(diǎn)未禁用,且關(guān)聯(lián)條件計算值為 true 的情況下,該忽略次數會(huì )遞減。

condition bpnumber [condition]?

為斷點(diǎn)設置一個(gè)新 condition,它是一個(gè)表達式,且它的計算值為 true 時(shí)斷點(diǎn)才起作用。如果沒(méi)有給出 condition,則刪除現有條件,也就是將斷點(diǎn)設為無(wú)條件。

commands [bpnumber]?

為編號是 bpnumber 的斷點(diǎn)指定一系列命令。命令內容將顯示在后續的幾行中。輸入僅包含 end 的行來(lái)結束命令列表。舉個(gè)例子:

(Pdb) commands 1
(com) p some_variable
(com) end
(Pdb)

要刪除斷點(diǎn)上的所有命令,請輸入 commands 并立即以 end 結尾,也就是不指定任何命令。

如果不帶 bpnumber 參數,commands 作用于最后一個(gè)被設置的斷點(diǎn)。

可以為斷點(diǎn)指定命令來(lái)重新啟動(dòng)程序。只需使用 continuestep 命令或其他可以繼續運行程序的命令。

如果指定了某個(gè)繼續運行程序的命令(目前包括 continue, step, next, return, jump, quit 及它們的縮寫(xiě))將終止命令列表(就像該命令后緊跟著(zhù) end)。因為在任何時(shí)候繼續運行下去(即使是簡(jiǎn)單的 next 或 step),都可能會(huì )遇到另一個(gè)斷點(diǎn),該斷點(diǎn)可能具有自己的命令列表,這導致要執行的列表含糊不清。

如果在命令列表中加入 'silent' 命令,那么在該斷點(diǎn)處停下時(shí)就不會(huì )打印常規信息。如果希望斷點(diǎn)打印特定信息后繼續運行,這可能是理想的。如果沒(méi)有其他命令來(lái)打印一些信息,則看不到已達到斷點(diǎn)的跡象。

s(tep)?

運行當前行,在第一個(gè)可以停止的位置(在被調用的函數內部或在當前函數的下一行)停下。

n(ext)?

繼續運行,直到運行到當前函數的下一行,或當前函數返回為止。( nextstep 之間的區別在于,step 進(jìn)入被調用函數內部并停止,而 next (幾乎)全速運行被調用函數,僅在當前函數的下一行停止。)

unt(il) [lineno]?

如果不帶參數,則繼續運行,直到行號比當前行大時(shí)停止。

如果帶有行號,則繼續運行,直到行號大于或等于該行號時(shí)停止。在這兩種情況下,當前幀返回時(shí)也將停止。

在 3.2 版更改: 允許明確給定行號。

r(eturn)?

繼續運行,直到當前函數返回。

c(ont(inue))?

繼續運行,僅在遇到斷點(diǎn)時(shí)停止。

j(ump) lineno?

設置即將運行的下一行。僅可用于堆棧最底部的幀。它可以往回跳來(lái)再次運行代碼,也可以往前跳來(lái)跳過(guò)不想運行的代碼。

需要注意的是,不是所有的跳轉都是允許的 -- 例如,不能跳轉到 for 循環(huán)的中間或跳出 finally 子句。

l(ist) [first[, last]]?

列出當前文件的源代碼。如果不帶參數,則列出當前行周?chē)?11 行,或繼續前一個(gè)列表。如果用 . 作為參數,則列出當前行周?chē)?11 行。如果帶有一個(gè)參數,則列出那一行周?chē)?11 行。如果帶有兩個(gè)參數,則列出所給的范圍中的代碼;如果第二個(gè)參數小于第一個(gè)參數,則將其解釋為列出行數的計數。

當前幀中的當前行用 -> 標記。如果正在調試異常,且最早拋出或傳遞該異常的行不是當前行,則那一行用 >> 標記。

3.2 新版功能: >> 標記。

ll | longlist?

列出當前函數或幀的所有源代碼。相關(guān)行的標記與 list 相同。

3.2 新版功能.

a(rgs)?

打印當前函數的參數列表。

p expression?

在當前上下文中運行 expression 并打印它的值。

備注

print() 也可以使用,但它不是一個(gè)調試器命令 --- 它執行 Python print() 函數。

pp expression?

p 命令類(lèi)似,但表達式的值使用 pprint 模塊美觀(guān)地打印。

whatis expression?

打印 expression 的類(lèi)型。

source expression?

嘗試獲取給定對象的源代碼并顯示出來(lái)。

3.2 新版功能.

display [expression]?

如果表達式的值發(fā)生改變則顯示它的值,每次將停止執行當前幀。

不帶表達式則列出當前幀的所有顯示表達式。

3.2 新版功能.

undisplay [expression]?

不再顯示當前幀中的表達式。 不帶表達式則清除當前幀的所有顯示表達式。

3.2 新版功能.

interact?

啟動(dòng)一個(gè)交互式解釋器(使用 code 模塊),它的全局命名空間將包含當前作用域中的所有(全局和局部)名稱(chēng)。

3.2 新版功能.

alias [name [command]]?

創(chuàng )建一個(gè)標識為 name 的別名來(lái)執行 command。 執行的命令 不可 加上引號。 可替換形參可通過(guò) %1, %2 等來(lái)標示,而 %* 會(huì )被所有形參所替換。 如果沒(méi)有給出命令,則會(huì )顯示 name 的當前別名。 如果沒(méi)有給出參數,則會(huì )列出所有別名。

別名允許嵌套并可包含能在 pdb 提示符下合法輸入的任何內容。 請注意內部 pdb 命令 可以 被別名所覆蓋。 這樣的命令將被隱藏直到別名被移除。 別名會(huì )遞歸地應用到命令行的第一個(gè)單詞;行內的其他單詞不會(huì )受影響。

作為示例,這里列出了兩個(gè)有用的別名(特別適合放在 .pdbrc 文件中):

# Print instance variables (usage "pi classInst")
alias pi for k in %1.__dict__.keys(): print("%1.",k,"=",%1.__dict__[k])
# Print instance variables in self
alias ps pi self
unalias name?

刪除指定的別名。

! statement?

在當前堆棧幀的上下文中執行 (單行) statement。 感嘆號可以被省略,除非語(yǔ)句的第一個(gè)單詞與調試器命令重名。 要設置全局變量,你可以在同一行上為賦值命令添加前綴的 global 語(yǔ)句,例如:

(Pdb) global list_options; list_options = ['-l']
(Pdb)
run [args ...]?
restart [args ...]?

重啟被調試的 Python 程序。 如果提供了參數,它會(huì )用 shlex 來(lái)拆分且拆分結果將被用作新的 sys.argv。 歷史、中斷點(diǎn)、動(dòng)作和調試器選項將被保留。 restartrun 的一個(gè)別名。

q(uit)?

退出調試器。 被執行的程序將被中止。

debug code?

進(jìn)入一個(gè)對代碼參數執行步進(jìn)的遞歸調試器(該參數是在當前環(huán)境中執行的任意表達式或語(yǔ)句)。

retval?

打印函數最后一次返回的返回值。

備注

1

一個(gè)幀是否會(huì )被認為源自特定模塊是由幀全局變量 __name__ 來(lái)決定的。