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

源代碼: Lib/pdb.py


pdb 模塊定義了一個交互式源代碼調(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ǔ)全會提供當(dāng)前的全局變量和局部變量,用作 p 命令的參數(shù)。

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

python3 -m pdb myscript.py

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

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

3.7 新版功能: pdb.py 現(xiàn)在接受 -m 選項(xiàng),該選項(xiàng)用于執(zhí)行一個模塊,類似于 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)用它時,可以用來代替 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ù),每個函數(shù)進(jìn)入調(diào)試器的方式略有不同:

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

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

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

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

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

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

pdb.set_trace(*, header=None)?

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

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

pdb.post_mortem(traceback=None)?

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

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)試器類。

completekeystdinstdout 參數(shù)都會傳遞給底層的 cmd.Cmd 類,請參考相應(yīng)的描述。

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

默認(rèn)情況下,當(dāng)發(fā)出 continue 命令時,Pdb 將為 SIGINT 信號設(shè)置一個處理程序(SIGINT 信號是用戶在控制臺按 Ctrl-C 時發(fā)出的)。這使用戶可以按 Ctrl-C 再次進(jìn)入調(diào)試器。如果希望 Pdb 不要修改 SIGINT 處理程序,請將 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ā)一個 審計(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()?

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

調(diào)試器命令?

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

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

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

調(diào)試器支持 別名。別名可以有參數(shù),使得調(diào)試器對被檢查的上下文有一定程度的適應(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ù)為 command 時,打印有關(guān)該命令的幫助。help pdb 顯示完整文檔(即 pdb 模塊的文檔字符串)。由于 command 參數(shù)必須是標(biāo)識符,因此要獲取 ! 的幫助必須輸入 help exec。

w(here)?

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

d(own) [count]?

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

u(p) [count]?

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

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

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

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

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

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

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

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

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

disable [bpnumber ...]?

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

enable [bpnumber ...]?

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

ignore bpnumber [count]?

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

condition bpnumber [condition]?

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

commands [bpnumber]?

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

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

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

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

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

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

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

s(tep)?

運(yùn)行當(dāng)前行,在第一個可以停止的位置(在被調(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)行,直到行號比當(dāng)前行大時停止。

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

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

r(eturn)?

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

c(ont(inue))?

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

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ù)前一個列表。如果用 . 作為參數(shù),則列出當(dāng)前行周圍的 11 行。如果帶有一個參數(shù),則列出那一行周圍的 11 行。如果帶有兩個參數(shù),則列出所給的范圍中的代碼;如果第二個參數(shù)小于第一個參數(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() 也可以使用,但它不是一個調(diào)試器命令 --- 它執(zhí)行 Python print() 函數(shù)。

pp expression?

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

whatis expression?

打印 expression 的類型。

source expression?

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

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?

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

3.2 新版功能.

alias [name [command]]?

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

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

作為示例,這里列出了兩個有用的別名(特別適合放在 .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。 感嘆號可以被省略,除非語句的第一個單詞與調(diào)試器命令重名。 要設(shè)置全局變量,你可以在同一行上為賦值命令添加前綴的 global 語句,例如:

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

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

q(uit)?

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

debug code?

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

retval?

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

備注

1

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