pdb --- Python的調試器?

源代碼: Lib/pdb.py


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

調試器是可擴展的——調試器實際被定義為 Pdb 類。該類目前沒有文檔,但通過閱讀源碼很容易理解它。擴展接口使用了 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 模塊實現的 Tab 補全可用于補全本模塊的命令和命令的參數,例如,p 命令的參數可補全為當前的全局變量和局部變量。

也可以將 pdb.py 作為腳本調用來調試其他腳本。例如:

python3 -m pdb myscript.py

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

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

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

從正在運行的程序進入調試器的典型用法是插入

import pdb; pdb.set_trace()

到你想進入調試器的位置。然后就可以單步執行上述語句之后的代碼,要關閉調試器繼續運行,請使用 continue 命令。

3.7 新版功能: 內置函數 breakpoint(),當以默認參數調用它時,可以用來代替 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)

本模塊定義了下列函數,每個函數進入調試器的方式略有不同:

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

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

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

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

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

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

pdb.set_trace(*, header=None)?

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

在 3.7 版更改: 僅關鍵字參數 header

pdb.post_mortem(traceback=None)?

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

pdb.pm()?

sys.last_traceback 中查找 traceback,并進入其事后調試。

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

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

Pdb 是調試器類。

completekeystdinstdout 參數都會傳遞給底層的 cmd.Cmd 類,請參考相應的描述。

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

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

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

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

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

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()?

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

調試器命令?

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

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

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

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

在一行中可以輸入多個命令,以 ;; 分隔。(不能使用單個 ;,因為它用于分隔傳遞給 Python 解釋器的一行中的多條語句。)切分命令很無腦,總是在第一個 ;; 對處將輸入切分開,即使它位于帶引號的字符串之中。

如果文件 .pdbrc 存在于用戶主目錄或當前目錄中,則將其讀入并執行,等同于在調試器提示符下鍵入該文件。這對于別名很有用。若兩個文件都存在則首先讀取主目錄中的文件,且本地文件可以覆蓋其中定義的別名。

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

h(elp) [command]?

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

w(here)?

打印堆棧回溯,最新一幀在底部。有一個箭頭指向當前幀,該幀決定了大多數命令的上下文。

d(own) [count]?

在堆棧回溯中,將當前幀向下移動 count 級(默認為 1 級,移向更新的幀)。

u(p) [count]?

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

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

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

如果第二個參數存在,它應該是一個表達式,且它的計算值為 true 時斷點才起作用。

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

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

臨時斷點,在第一次命中時會自動刪除。它的參數與 break 相同。

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

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

disable [bpnumber [bpnumber ...]]?

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

enable [bpnumber [bpnumber ...]]?

啟用指定的斷點。

ignore bpnumber [count]?

為指定的斷點編號設置忽略次數。如果省略 count,則忽略次數將設置為 0。忽略次數為 0 時斷點將變為活動狀態。如果為非零值,在每次達到斷點,且斷點未禁用,且關聯條件計算值為 true 的情況下,該忽略次數會遞減。

condition bpnumber [condition]?

為斷點設置一個新 condition,它是一個表達式,且它的計算值為 true 時斷點才起作用。如果沒有給出 condition,則刪除現有條件,也就是將斷點設為無條件。

commands [bpnumber]?

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

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

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

如果不帶 bpnumber 參數,commands 作用于最后一個被設置的斷點。

可以使用斷點命令來重新啟動程序。只需使用 continuestep 命令或其他可以恢復運行的命令。

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

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

s(tep)?

執行當前行,在第一個可以停止的位置(在調用的函數中或在當前函數的下一行)停下。

n(ext)?

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

unt(il) [lineno]?

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

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

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

r(eturn)?

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

c(ont(inue))?

繼續運行,僅在遇到斷點時停止。

j(ump) lineno?

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

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

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

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

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

3.2 新版功能: >> 標記。

ll | longlist?

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

3.2 新版功能.

a(rgs)?

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

p expression?

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

注解

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

pp expression?

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

whatis expression?

打印 expression 的類型。

source expression?

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

3.2 新版功能.

display [expression]?

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

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

3.2 新版功能.

undisplay [expression]?

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

3.2 新版功能.

interact?

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

3.2 新版功能.

alias [name [command]]?

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

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

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

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

重啟被調試的 Python 程序。 如果提供了參數,它會用 shlex 來拆分且拆分結果將被用作新的 sys.argv。 歷史、中斷點、動作和調試器選項將被保留。 restartrun 的一個別名。

q(uit)?

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

debug code?

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

retval?
Print the return value for the last return of a function.

備注

1

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