pdb
— Python 调试器¶
源代码: Lib/pdb.py
模块 pdb
为 Python 程序定义了一个交互式源代码调试器。它支持在源代码行级别设置(条件)断点和单步执行、检查堆栈帧、列出源代码以及在任何堆栈帧的上下文中评估任意 Python 代码。它还支持事后调试,并可以由程序控制调用。
该调试器是可扩展的——它实际上被定义为 Pdb
类。这一点目前没有文档说明,但通过阅读源代码很容易理解。其扩展接口使用了 bdb
和 cmd
模块。
参见
- 模块
faulthandler
用于在发生故障、超时或收到用户信号时,显式地转储 Python 回溯信息。
- 模块
traceback
用于提取、格式化和打印 Python 程序堆栈回溯信息的标准接口。
进入调试器的典型用法是在你想进入调试器的位置插入
import pdb; pdb.set_trace()
或
breakpoint()
然后运行程序。之后你可以单步执行该语句之后的代码,并使用 continue
命令继续运行而不带调试器。
在 3.7 版更改: 内置函数 breakpoint()
在使用默认参数调用时,可以替代 import pdb; pdb.set_trace()
。
def double(x):
breakpoint()
return x * 2
val = 3
print(f"{val} * 2 is {double(val)}")
调试器的提示符是 (Pdb)
,它表示你正处于调试模式
> ...(2)double()
-> breakpoint()
(Pdb) p x
3
(Pdb) continue
3 * 2 is 6
在 3.3 版更改: 通过 readline
模块实现了命令和命令参数的 Tab 补全功能,例如,当前的全局和局部名称会作为 p
命令的参数提供。
你也可以从命令行调用 pdb
来调试其他脚本。例如:
python -m pdb [-c command] (-m module | -p pid | pyfile) [args ...]
当作为模块调用时,如果被调试的程序异常退出,pdb 将自动进入事后调试模式。在事后调试之后(或程序正常退出后),pdb 将重新启动程序。自动重启会保留 pdb 的状态(例如断点),在大多数情况下,这比在程序退出时退出调试器更有用。
- -m <module>¶
以类似于
python -m
的方式执行模块。与脚本一样,调试器将在模块的第一行之前暂停执行。在 3.7 版更改: 添加了
-m
选项。
- -p, --pid <pid>¶
附加到指定 PID 的进程。
在 3.14 版本加入。
要附加到一个正在运行的 Python 进程进行远程调试,请使用 -p
或 --pid
选项并提供目标进程的 PID:
python -m pdb -p 1234
备注
附加到一个在系统调用中阻塞或等待 I/O 的进程,只有在下一个字节码指令被执行或进程收到信号时才会生效。
在调试器控制下执行一条语句的典型用法是:
>>> import pdb
>>> def f(x):
... print(1 / x)
>>> pdb.run("f(2)")
> <string>(1)<module>()
(Pdb) continue
0.5
>>>
检查崩溃程序的典型用法是:
>>> import pdb
>>> def f(x):
... print(1 / x)
...
>>> f(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
ZeroDivisionError: division by zero
>>> pdb.pm()
> <stdin>(2)f()
(Pdb) p x
0
(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()
返回时,它返回 expression 的值。否则,此函数类似于run()
。
- pdb.runcall(function, *args, **kwds)¶
使用给定的参数调用 function(一个函数或方法对象,而不是字符串)。当
runcall()
返回时,它返回函数调用的返回值。调试器提示符在函数进入时立即出现。
- pdb.set_trace(*, header=None, commands=None)¶
在调用堆栈帧处进入调试器。这对于在程序中的给定点硬编码一个断点非常有用,即使代码没有以其他方式进行调试(例如,当断言失败时)。如果给出 header,它会在调试开始前打印到控制台。如果给出 commands 参数,它是一个在调试器启动时要执行的命令列表。
在 3.7 版更改: 仅限关键字参数 header。
在 3.13 版更改:
set_trace()
将立即进入调试器,而不是在下一行要执行的代码处。3.14 版新加入: commands 参数。
- awaitable pdb.set_trace_async(*, header=None, commands=None)¶
set_trace()
的异步版本。此函数应在异步函数内与await
一起使用。async def f(): await pdb.set_trace_async()
如果调试器由此函数调用,则支持
await
语句。在 3.14 版本加入。
- pdb.post_mortem(t=None)¶
进入给定异常或 回溯对象 的事后调试。如果未提供值,则使用当前正在处理的异常,如果没有异常,则引发
ValueError
。在 3.13 版更改: 添加了对异常对象的支持。
- pdb.pm()¶
进入在
sys.last_exc
中找到的异常的事后调试。
- pdb.set_default_backend(backend)¶
pdb 有两个受支持的后端:
'settrace'
和'monitoring'
。详情请参见bdb.Bdb
。用户可以在实例化Pdb
时设置要使用的默认后端,如果未指定。如果未指定后端,默认值为'settrace'
。备注
breakpoint()
和set_trace()
不会受此函数影响。它们总是使用'monitoring'
后端。在 3.14 版本加入。
- pdb.get_default_backend()¶
返回 pdb 的默认后端。
在 3.14 版本加入。
run*
函数和 set_trace()
是实例化 Pdb
类并调用同名方法的别名。如果你想访问更多功能,你必须自己动手:
- class pdb.Pdb(completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True, mode=None, backend=None, colorize=False)¶
Pdb
是调试器类。completekey、stdin 和 stdout 参数被传递给底层的
cmd.Cmd
类;请参阅那里的描述。skip 参数(如果给出)必须是 glob 风格模块名模式的可迭代对象。调试器不会进入源于匹配这些模式之一的模块的帧。[1]
默认情况下,当你给出
continue
命令时,Pdb 会为 SIGINT 信号(当用户在控制台上按 Ctrl-C 时发送)设置一个处理程序。这允许你通过按 Ctrl-C 再次进入调试器。如果你希望 Pdb 不触碰 SIGINT 处理程序,请将 nosigint 设置为 true。readrc 参数默认为 true,控制 Pdb 是否会从文件系统加载 .pdbrc 文件。
mode 参数指定调试器是如何被调用的。它会影响一些调试器命令的工作方式。有效值为
'inline'
(由 breakpoint() 内置函数使用)、'cli'
(由命令行调用使用)或None
(用于向后兼容的行为,如同添加 mode 参数之前一样)。backend 参数指定调试器使用的后端。如果传入
None
,将使用默认后端。参见set_default_backend()
。否则,支持的后端是'settrace'
和'monitoring'
。colorize 参数,如果设置为
True
,将在调试器中启用彩色输出(如果支持颜色的话)。这将高亮显示 pdb 中显示的源代码。启用带有 skip 的跟踪的示例调用:
import pdb; pdb.Pdb(skip=['django.*']).set_trace()
引发一个不带参数的 审计事件
pdb.Pdb
。在 3.1 版更改: 添加了 skip 参数。
在 3.2 版更改: 添加了 nosigint 参数。以前,Pdb 从未设置过 SIGINT 处理程序。
在 3.6 版更改: readrc 参数。
3.14 版新加入: 添加了 mode 参数。
3.14 版新加入: 添加了 backend 参数。
3.14 版新加入: 添加了 colorize 参数。
在 3.14 版更改: 像
breakpoint()
或pdb.set_trace()
这样的内联断点将始终在调用帧处停止程序,忽略 skip 模式(如果有的话)。
调试器命令¶
调试器识别的命令如下所示。大多数命令可以缩写为一个或两个字母,如所示;例如,h(elp)
意味着可以使用 h
或 help
来输入帮助命令(但不能是 he
或 hel
,也不能是 H
、Help
或 HELP
)。命令的参数必须用空白(空格或制表符)分隔。可选参数在命令语法中用方括号([]
)括起来;方括号本身不应输入。命令语法中的备选项用竖线(|
)分隔。
输入一个空行会重复上一个输入的命令。例外:如果上一个命令是 list
命令,则会列出接下来的 11 行。
调试器不认识的命令被假定为 Python 语句,并在被调试程序的上下文中执行。Python 语句也可以用感叹号(!
)作为前缀。这是检查被调试程序的一种强大方式;甚至可以更改变量或调用函数。当此类语句中发生异常时,会打印异常名称,但调试器的状态不会改变。
在 3.13 版更改: 前缀是 pdb 命令的表达式/语句现在可以被正确识别和执行。
调试器支持别名。别名可以有参数,这使得它在一定程度上能够适应被检查的上下文。
可以在单行中输入多个命令,用 ;;
分隔。(单个 ;
不被使用,因为它是传递给 Python 解析器的一行中多个命令的分隔符。)在分隔命令时没有应用任何智能;输入在第一个 ;;
对处被分割,即使它位于带引号的字符串中间。对于包含双分号的字符串,一个变通方法是使用隐式字符串拼接 ';'';'
或 ";"";"
。
要设置一个临时的全局变量,请使用*便捷变量*。*便捷变量*是一个名称以 $
开头的变量。例如,$foo = 1
设置了一个全局变量 $foo
,你可以在调试会话中使用它。当程序恢复执行时,*便捷变量*会被清除,因此与使用普通变量(如 foo = 1
)相比,它不太可能干扰你的程序。
有四个预设的*便捷变量*:
$_frame
:你正在调试的当前帧$_retval
:如果帧正在返回,则为返回值$_exception
:如果帧正在引发异常,则为该异常$_asynctask
:如果 pdb 在异步函数中停止,则为 asyncio 任务
3.12 版新加入: 添加了*便捷变量*功能。
3.14 版新加入: 添加了 $_asynctask
便捷变量。
如果用户的家目录或当前目录中存在文件 .pdbrc
,它将以 'utf-8'
编码读取并执行,就像在调试器提示符下输入一样,但空行和以 #
开头的行将被忽略。这对别名特别有用。如果两个文件都存在,则首先读取家目录中的文件,并且其中定义的别名可以被本地文件覆盖。
在 3.11 版更改: .pdbrc
现在以 'utf-8'
编码读取。以前,它以系统区域设置编码读取。
- h(elp) [command]¶
不带参数时,打印可用命令的列表。带一个 command 作为参数时,打印关于该命令的帮助信息。
help pdb
显示完整的文档(pdb
模块的文档字符串)。由于 command 参数必须是标识符,因此必须输入help exec
来获取关于!
命令的帮助。
- w(here) [count]¶
打印堆栈跟踪,最新的帧在底部。如果 count 为 0,打印当前帧条目。如果 count 为负数,打印最旧的 -count 个帧。如果 count 为正数,打印最新的 count 个帧。一个箭头(
>
)指示当前帧,它决定了大多数命令的上下文。在 3.14 版更改: 添加了 count 参数。
- d(own) [count]¶
在堆栈跟踪中将当前帧向下移动 count(默认为 1)层(到一个较新的帧)。
- u(p) [count]¶
在堆栈跟踪中将当前帧向上移动 count(默认为 1)层(到一个较旧的帧)。
- b(reak) [([filename:]lineno | function) [, condition]]¶
带 lineno 参数时,在当前文件的第 lineno 行设置一个断点。行号可以加上 filename 和冒号作为前缀,以在另一个文件中指定断点(可能是一个尚未加载的文件)。该文件将在
sys.path
上搜索。可接受的 filename 形式有/abspath/to/file.py
、relpath/file.py
、module
和package.module
。带 function 参数时,在该函数的第一个可执行语句处设置断点。function 可以是任何在当前命名空间中计算结果为函数的表达式。
如果存在第二个参数,它是一个表达式,必须评估为 true,断点才会被触发。
不带参数时,列出所有断点,包括每个断点被命中的次数、当前的忽略计数以及任何相关的条件。
每个断点都被分配一个编号,所有其他断点命令都通过这个编号来引用它。
- cl(ear) [filename:lineno | bpnumber ...]¶
带 filename:lineno 参数时,清除此行的所有断点。带一个以空格分隔的断点编号列表时,清除那些断点。不带参数时,清除所有断点(但会先请求确认)。
- disable bpnumber [bpnumber ...]¶
禁用以空格分隔的断点编号列表所给出的断点。禁用断点意味着它不能导致程序停止执行,但与清除断点不同,它仍然保留在断点列表中,并且可以被(重新)启用。
- enable bpnumber [bpnumber ...]¶
启用指定的断点。
- ignore bpnumber [count]¶
为给定的断点编号设置忽略计数。如果省略 count,则忽略计数设置为 0。当忽略计数为零时,断点变为活动状态。当非零时,每次到达断点并且断点未被禁用且任何相关条件评估为 true 时,count 会递减。
- condition bpnumber [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
、until
、jump
、quit
及其缩写)会终止命令列表(就好像该命令后面立即跟了 end)。这是因为任何时候你恢复执行(即使是简单的 next 或 step),你都可能遇到另一个断点——它可能有自己的命令列表,导致关于执行哪个列表的歧义。如果命令列表中包含
silent
命令,或者一个恢复执行的命令,那么包含帧信息的断点消息将不会显示。在 3.14 版更改: 如果命令列表中存在恢复执行的命令,则不会显示帧信息。
- s(tep)¶
执行当前行,在第一个可能的机会停下来(无论是在被调用的函数中,还是在当前函数的下一行)。
- n(ext)¶
继续执行,直到到达当前函数的下一行或函数返回。(
next
和step
的区别在于,step
会在被调用的函数内部停止,而next
会以(几乎)全速执行被调用的函数,只在当前函数的下一行停止。)
- unt(il) [lineno]¶
不带参数时,继续执行直到到达行号大于当前行的那一行。
带 lineno 时,继续执行直到到达行号大于或等于 lineno 的那一行。在这两种情况下,当当前帧返回时也会停止。
在 3.2 版更改: 允许给出一个明确的行号。
- r(eturn)¶
继续执行,直到当前函数返回。
- c(ont(inue))¶
继续执行,只在遇到断点时停止。
- j(ump) lineno¶
设置下一行将要执行的代码。只在最底层的帧中可用。这让你能跳回并再次执行代码,或者向前跳过你不想运行的代码。
- l(ist) [first[, last]]¶
列出当前文件的源代码。不带参数时,列出当前行周围的 11 行或继续上一次的列表。以
.
作为参数时,列出当前行周围的 11 行。带一个参数时,列出该行周围的 11 行。带两个参数时,列出给定的范围;如果第二个参数小于第一个,它被解释为一个计数。当前帧中的当前行由
->
指示。如果正在调试一个异常,异常最初被引发或传播的行由>>
指示,如果它与当前行不同。在 3.2 版更改: 添加了
>>
标记。
- a(rgs)¶
打印当前函数的参数及其当前值。
- whatis expression¶
打印 expression 的类型。
- source expression¶
尝试获取 expression 的源代码并显示它。
在 3.2 版本加入。
- display [expression]¶
如果 expression 的值发生变化,每次在当前帧中停止执行时显示其值。
不带 expression 时,列出当前帧的所有 display 表达式。
备注
Display 会评估 expression 并将其与上次评估 expression 的结果进行比较,因此当结果是可变的时,display 可能无法捕捉到变化。
示例
lst = [] breakpoint() pass lst.append(1) print(lst)
Display 不会意识到
lst
已经被改变,因为评估结果在被比较之前被lst.append(1)
就地修改了。> example.py(3)<module>() -> pass (Pdb) display lst display lst: [] (Pdb) n > example.py(4)<module>() -> lst.append(1) (Pdb) n > example.py(5)<module>() -> print(lst) (Pdb)
你可以用一些复制机制的技巧来让它工作:
> example.py(3)<module>() -> pass (Pdb) display lst[:] display lst[:]: [] (Pdb) n > example.py(4)<module>() -> lst.append(1) (Pdb) n > example.py(5)<module>() -> print(lst) display lst[:]: [1] [old: []] (Pdb)
在 3.2 版本加入。
- undisplay [expression]¶
不再在当前帧中显示 expression。不带 expression 时,清除当前帧的所有 display 表达式。
在 3.2 版本加入。
- interact¶
在一个新的全局命名空间中启动一个交互式解释器(使用
code
模块),该命名空间从当前作用域的局部和全局命名空间初始化。使用exit()
或quit()
退出解释器并返回到调试器。备注
由于
interact
为代码执行创建了一个新的专用命名空间,对变量的赋值不会影响原始命名空间。但是,对任何引用的可变对象的修改将照常反映在原始命名空间中。在 3.2 版本加入。
在 3.13 版更改: 可以使用
exit()
和quit()
退出interact
命令。在 3.13 版更改:
interact
将其输出定向到调试器的输出通道,而不是sys.stderr
。
- alias [name [command]]¶
创建一个名为 name 的别名,用于执行 command。command 必须*不*用引号括起来。可替换的参数可以用
%1
、%2
、... 和%9
来表示,而%*
被所有参数替换。如果省略 command,则显示 name 的当前别名。如果不给出任何参数,则列出所有别名。别名可以嵌套,并且可以包含任何可以合法地在 pdb 提示符下输入的内容。请注意,内部的 pdb 命令*可以*被别名覆盖。这样的命令就会被隐藏,直到别名被移除。别名递归地应用于命令行的第一个词;行中的所有其他词都保持不变。
作为一个例子,这里有两个有用的别名(尤其是在放在
.pdbrc
文件中时):# Print instance variables (usage "pi classInst") alias pi for k in %1.__dict__.keys(): print(f"%1.{k} = {%1.__dict__[k]}") # Print instance variables in self alias ps pi self
- unalias name¶
删除指定的别名 name。
- ! statement¶
在当前堆栈帧的上下文中执行(单行)statement。除非语句的第一个词与调试器命令相似,否则可以省略感叹号,例如:
(Pdb) ! n=42 (Pdb)
要设置一个全局变量,你可以在同一行上用
global
语句作为赋值命令的前缀,例如:(Pdb) global list_options; list_options = ['-l'] (Pdb)
- run [args ...]¶
- restart [args ...]¶
重新启动被调试的 Python 程序。如果提供了 args,它会用
shlex
进行分割,结果用作新的sys.argv
。历史、断点、动作和调试器选项都会被保留。restart
是run
的一个别名。
- q(uit)¶
退出调试器。正在执行的程序将被中止。文件结束输入等同于
quit
。如果调试器以
'inline'
模式调用,将显示一个确认提示。y
、Y
、<Enter>
或EOF
都将确认退出。在 3.14 版更改: 如果调试器以
'inline'
模式调用,将显示一个确认提示。确认后,调试器将立即调用sys.exit()
,而不是在下一个跟踪事件中引发bdb.BdbQuit
。
- debug code¶
进入一个递归调试器,单步执行 code(它是在当前环境中执行的任意表达式或语句)。
- retval¶
打印当前函数最后一次返回的返回值。
- exceptions [excnumber]¶
列出或在链式异常之间跳转。
当使用
pdb.pm()
或Pdb.post_mortem(...)
处理链式异常而不是回溯时,它允许用户使用exceptions
命令列出异常,并使用exceptions <number>
切换到该异常。示例
def out(): try: middle() except Exception as e: raise ValueError("reraise middle() error") from e def middle(): try: return inner(0) except Exception as e: raise ValueError("Middle fail") def inner(x): 1 / x out()
调用
pdb.pm()
将允许在异常之间移动> example.py(5)out() -> raise ValueError("reraise middle() error") from e (Pdb) exceptions 0 ZeroDivisionError('division by zero') 1 ValueError('Middle fail') > 2 ValueError('reraise middle() error') (Pdb) exceptions 0 > example.py(16)inner() -> 1 / x (Pdb) up > example.py(10)middle() -> return inner(0)
在 3.13 版本加入。
脚注