pdb --- Python的調試器?
源代碼: Lib/pdb.py
pdb 模塊定義了一個交互式源代碼調試器,用于 Python 程序。它支持在源碼行間設置(有條件的)斷點和單步執行,檢視堆棧幀,列出源碼列表,以及在任何堆棧幀的上下文中運行任意 Python 代碼。它還支持事后調試,可以在程序控制下調用。
調試器是可擴展的——調試器實際被定義為 Pdb 類。該類目前沒有文檔,但通過閱讀源碼很容易理解它。擴展接口使用了 bdb 和 cmd 模塊。
調試器的提示符是 (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,也可以使用step或next逐步執行語句(上述所有命令在后文有說明)。可選參數 globals 和 locals 指定代碼執行環境,默認時使用__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是調試器類。completekey、stdin 和 stdout 參數都會傳遞給底層的
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 參數。
調試器命令?
下方列出的是調試器可接受的命令。如下所示,大多數命令可以縮寫為一個或兩個字母。如 h(elp) 表示可以輸入 h 或 help 來輸入幫助命令(但不能輸入 he 或 hel,也不能是 H 或 Help 或 HELP)。命令的參數必須用空格(空格符或制表符)分隔。在命令語法中,可選參數括在方括號 ([]) 中,使用時請勿輸入方括號。命令語法中的選擇項由豎線 (|) 分隔。
輸入一個空白行將重復最后輸入的命令。例外:如果最后一個命令是 list 命令,則會列出接下來的 11 行。
調試器無法識別的命令將被認為是 Python 語句,并在正在調試的程序的上下文中執行。Python 語句也可以用感嘆號 (!) 作為前綴。這是檢查正在調試的程序的強大方法,甚至可以修改變量或調用函數。當此類語句發生異常,將打印異常名稱,但調試器的狀態不會改變。
調試器支持 別名。別名可以有參數,使得調試器對被檢查的上下文有一定程度的適應性。
在一行中可以輸入多個命令,以 ;; 分隔。(不能使用單個 ;,因為它用于分隔傳遞給 Python 解釋器的一行中的多條語句。)切分命令很無腦,總是在第一個 ;; 對處將輸入切分開,即使它位于帶引號的字符串之中。
如果文件 .pdbrc 存在于用戶主目錄或當前目錄中,則將其讀入并執行,等同于在調試器提示符下鍵入該文件。這對于別名很有用。若兩個文件都存在則首先讀取主目錄中的文件,且本地文件可以覆蓋其中定義的別名。
-
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 時斷點才起作用。
如果不帶參數執行,將列出所有中斷,包括每個斷點、命中該斷點的次數、當前的忽略次數以及關聯的條件(如果有)。
-
cl(ear)[filename:lineno | bpnumber [bpnumber ...]]? 如果參數是 filename:lineno,則清除此行上的所有斷點。如果參數是空格分隔的斷點編號列表,則清除這些斷點。如果不帶參數,則清除所有斷點(但會先提示確認)。
-
disable[bpnumber [bpnumber ...]]? 禁用斷點,斷點以空格分隔的斷點編號列表給出。禁用斷點表示它不會導致程序停止執行,但是與清除斷點不同,禁用的斷點將保留在斷點列表中并且可以(重新)啟用。
-
enable[bpnumber [bpnumber ...]]? 啟用指定的斷點。
-
ignorebpnumber [count]? 為指定的斷點編號設置忽略次數。如果省略 count,則忽略次數將設置為 0。忽略次數為 0 時斷點將變為活動狀態。如果為非零值,在每次達到斷點,且斷點未禁用,且關聯條件計算值為 true 的情況下,該忽略次數會遞減。
-
conditionbpnumber [condition]? 為斷點設置一個新 condition,它是一個表達式,且它的計算值為 true 時斷點才起作用。如果沒有給出 condition,則刪除現有條件,也就是將斷點設為無條件。
-
commands[bpnumber]? 為編號是 bpnumber 的斷點指定一系列命令。命令內容將顯示在后續的幾行中。輸入僅包含
end的行來結束命令列表。舉個例子:(Pdb) commands 1 (com) p some_variable (com) end (Pdb)
要刪除斷點上的所有命令,請輸入
commands并立即以end結尾,也就是不指定任何命令。如果不帶 bpnumber 參數,
commands作用于最后一個被設置的斷點。可以使用斷點命令來重新啟動程序。只需使用
continue或step命令或其他可以恢復運行的命令。如果指定了某個繼續運行程序的命令(目前包括
continue,step,next,return,jump,quit及它們的縮寫)將終止命令列表(就像該命令后緊跟著 end)。因為在任何時候繼續運行下去(即使是簡單的 next 或 step),都可能會遇到另一個斷點,該斷點可能具有自己的命令列表,這導致要執行的列表含糊不清。如果在命令列表中加入 'silent' 命令,那么在該斷點處停下時就不會打印常規信息。如果希望斷點打印特定信息后繼續運行,這可能是理想的。如果沒有其他命令來打印一些信息,則看不到已達到斷點的跡象。
-
s(tep)? 執行當前行,在第一個可以停止的位置(在調用的函數中或在當前函數的下一行)停下。
-
n(ext)? 繼續運行,直到運行到當前函數的下一行,或當前函數返回為止。(
next和step之間的區別在于,step進入被調用函數內部并停止,而next(幾乎)全速運行被調用函數,僅在當前函數的下一行停止。)
-
unt(il)[lineno]? 如果不帶參數,則繼續運行,直到行號比當前行大時停止。
如果帶有行號,則繼續運行,直到行號大于或等于該行號時停止。在這兩種情況下,當前幀返回時也將停止。
在 3.2 版更改: 允許明確給定行號。
-
r(eturn)? 繼續運行,直到當前函數返回。
-
c(ont(inue))? 繼續運行,僅在遇到斷點時停止。
-
j(ump)lineno? 設置即將運行的下一行。僅可用于堆棧最底部的幀。它可以往回跳來再次運行代碼,也可以往前跳來跳過不想運行的代碼。
-
l(ist)[first[, last]]? 列出當前文件的源代碼。如果不帶參數,則列出當前行周圍的 11 行,或繼續前一個列表。如果用
.作為參數,則列出當前行周圍的 11 行。如果帶有一個參數,則列出那一行周圍的 11 行。如果帶有兩個參數,則列出所給的范圍中的代碼;如果第二個參數小于第一個參數,則將其解釋為列出行數的計數。當前幀中的當前行用
->標記。如果正在調試異常,且最早拋出或傳遞該異常的行不是當前行,則那一行用>>標記。3.2 新版功能:
>>標記。
-
a(rgs)? 打印當前函數的參數列表。
-
whatisexpression? 打印 expression 的類型。
-
sourceexpression? 嘗試獲取給定對象的源代碼并顯示出來。
3.2 新版功能.
-
display[expression]? 如果表達式的值發生改變則顯示它的值,每次將停止執行當前幀。
不帶表達式則列出當前幀的所有顯示表達式。
3.2 新版功能.
-
undisplay[expression]? 不再顯示當前幀中的表達式。 不帶表達式則清除當前幀的所有顯示表達式。
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
-
unaliasname? 刪除指定的別名。
-
!statement? 在當前堆棧幀的上下文中執行 (單行) statement。 感嘆號可以被省略,除非語句的第一個單詞與調試器命令重名。 要設置全局變量,你可以在同一行上為賦值命令添加前綴的
global語句,例如:(Pdb) global list_options; list_options = ['-l'] (Pdb)
-
run[args ...]? -
restart[args ...]? 重啟被調試的 Python 程序。 如果提供了參數,它會用
shlex來拆分且拆分結果將被用作新的sys.argv。 歷史、中斷點、動作和調試器選項將被保留。restart是run的一個別名。
-
q(uit)? 退出調試器。 被執行的程序將被中止。
-
debugcode? 進入一個對代碼參數執行步進的遞歸調試器(該參數是在當前環境中執行的任意表達式或語句)。
-
retval? -
Print the return value for the last return of a function.
備注
- 1
一個幀是否會被認為源自特定模塊是由幀全局變量
__name__來決定的。
