pdb --- Python 的調(diào)試器?

源代碼: Lib/pdb.py


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

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

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

>>>
>>> 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í)現(xiàn)的 Tab 補(bǔ)全可用于補(bǔ)全本模塊的命令和命令的參數(shù),例如,Tab 補(bǔ)全會(huì)提供當(dāng)前的全局變量和局部變量,用作 p 命令的參數(shù)。

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

python3 -m pdb myscript.py

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

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

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

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 新版功能: 內(nèi)置函數(shù) breakpoint(),當(dāng)以默認(rèn)參數(shù)調(diào)用它時(shí),可以用來代替 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)

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

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

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

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

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

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

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

pdb.set_trace(*, header=None)?

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

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

pdb.post_mortem(traceback=None)?

進(jìn)入 traceback 對(duì)象的事后調(diào)試。如果沒有給定 traceback,默認(rèn)使用當(dāng)前正在處理的異常之一(默認(rèn)時(shí),必須存在正在處理的異常)。

pdb.pm()?

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

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

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

Pdb 是調(diào)試器類。

completekey、stdinstdout 參數(shù)都會(huì)傳遞給底層的 cmd.Cmd 類,請(qǐng)參考相應(yīng)的描述。

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

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

readrc 參數(shù)默認(rèn)為 true,它控制 Pdb 是否從文件系統(tǒng)加載 .pdbrc 文件。

啟用跟蹤且?guī)в?skip 參數(shù)的調(diào)用示范:

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

引發(fā)一個(gè) 審計(jì)事件 pdb.Pdb,沒有附帶參數(shù)。

3.1 新版功能: skip 參數(shù)。

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

在 3.6 版更改: readrc 參數(shù)。

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

請(qǐng)參閱上文解釋同名函數(shù)的文檔。

調(diào)試器命令?

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

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

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

調(diào)試器支持 別名。別名可以有參數(shù),使得調(diào)試器對(duì)被檢查的上下文有一定程度的適應(yīng)性。

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 現(xiàn)在可以包含繼續(xù)調(diào)試的命令,如 continuenext。文件中的這些命令以前是無效的。

h(elp) [command]?

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

w(here)?

打印堆?;厮荩钚乱粠诘撞?。有一個(gè)箭頭指向當(dāng)前幀,該幀決定了大多數(shù)命令的上下文。

d(own) [count]?

在堆棧回溯中,將當(dāng)前幀向下移動(dòng) count 級(jí)(默認(rèn)為 1 級(jí),移向更新的幀)。

u(p) [count]?

在堆棧回溯中,將當(dāng)前幀向上移動(dòng) count 級(jí)(默認(rèn)為 1 級(jí),移向更老的幀)。

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

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

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

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

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

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

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

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

disable [bpnumber ...]?

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

enable [bpnumber ...]?

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

ignore bpnumber [count]?

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

condition bpnumber [condition]?

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

commands [bpnumber]?

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

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

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

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

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

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

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

s(tep)?

運(yùn)行當(dāng)前行,在第一個(gè)可以停止的位置(在被調(diào)用的函數(shù)內(nèi)部或在當(dāng)前函數(shù)的下一行)停下。

n(ext)?

繼續(xù)運(yùn)行,直到運(yùn)行到當(dāng)前函數(shù)的下一行,或當(dāng)前函數(shù)返回為止。( nextstep 之間的區(qū)別在于,step 進(jìn)入被調(diào)用函數(shù)內(nèi)部并停止,而 next (幾乎)全速運(yùn)行被調(diào)用函數(shù),僅在當(dāng)前函數(shù)的下一行停止。)

unt(il) [lineno]?

如果不帶參數(shù),則繼續(xù)運(yùn)行,直到行號(hào)比當(dāng)前行大時(shí)停止。

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

在 3.2 版更改: 允許明確給定行號(hào)。

r(eturn)?

繼續(xù)運(yùn)行,直到當(dāng)前函數(shù)返回。

c(ont(inue))?

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

j(ump) lineno?

設(shè)置即將運(yùn)行的下一行。僅可用于堆棧最底部的幀。它可以往回跳來再次運(yùn)行代碼,也可以往前跳來跳過不想運(yùn)行的代碼。

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

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

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

當(dāng)前幀中的當(dāng)前行用 -> 標(biāo)記。如果正在調(diào)試異常,且最早拋出或傳遞該異常的行不是當(dāng)前行,則那一行用 >> 標(biāo)記。

3.2 新版功能: >> 標(biāo)記。

ll | longlist?

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

3.2 新版功能.

a(rgs)?

打印當(dāng)前函數(shù)的參數(shù)列表。

p expression?

在當(dāng)前上下文中運(yùn)行 expression 并打印它的值。

備注

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

pp expression?

p 命令類似,但表達(dá)式的值使用 pprint 模塊美觀地打印。

whatis expression?

打印 expression 的類型。

source expression?

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

3.2 新版功能.

display [expression]?

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

不帶表達(dá)式則列出當(dāng)前幀的所有顯示表達(dá)式。

3.2 新版功能.

undisplay [expression]?

不再顯示當(dāng)前幀中的表達(dá)式。 不帶表達(dá)式則清除當(dāng)前幀的所有顯示表達(dá)式。

3.2 新版功能.

interact?

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

3.2 新版功能.

alias [name [command]]?

創(chuàng)建一個(gè)標(biāo)識(shí)為 name 的別名來執(zhí)行 command。 執(zhí)行的命令 不可 加上引號(hào)。 可替換形參可通過 %1, %2 等來標(biāo)示,而 %* 會(huì)被所有形參所替換。 如果沒有給出命令,則會(huì)顯示 name 的當(dāng)前別名。 如果沒有給出參數(shù),則會(huì)列出所有別名。

別名允許嵌套并可包含能在 pdb 提示符下合法輸入的任何內(nèi)容。 請(qǐng)注意內(nèi)部 pdb 命令 可以 被別名所覆蓋。 這樣的命令將被隱藏直到別名被移除。 別名會(huì)遞歸地應(yīng)用到命令行的第一個(gè)單詞;行內(nèi)的其他單詞不會(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?

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

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

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

q(uit)?

退出調(diào)試器。 被執(zhí)行的程序?qū)⒈恢兄埂?/p>

debug code?

進(jìn)入一個(gè)對(duì)代碼參數(shù)執(zhí)行步進(jìn)的遞歸調(diào)試器(該參數(shù)是在當(dāng)前環(huán)境中執(zhí)行的任意表達(dá)式或語句)。

retval?

打印函數(shù)最后一次返回的返回值。

備注

1

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