pdb — Python 调试器

源代码: Lib/pdb.py


模块 pdb 为 Python 程序定义了一个交互式源代码调试器。它支持在源代码行级别设置(条件)断点和单步执行、检查堆栈帧、列出源代码以及在任何堆栈帧的上下文中评估任意 Python 代码。它还支持事后调试,并可以由程序控制调用。

该调试器是可扩展的——它实际上被定义为 Pdb 类。这一点目前没有文档说明,但通过阅读源代码很容易理解。其扩展接口使用了 bdbcmd 模块。

参见

模块 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 的状态(例如断点),在大多数情况下,这比在程序退出时退出调试器更有用。

-c, --command <command>

执行命令,如同这些命令是在 .pdbrc 文件中给出的一样;参见 调试器命令

在 3.2 版更改: 添加了 -c 选项。

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

在 3.13 版更改: PEP 667 的实现意味着通过 pdb 进行的名称赋值将立即影响活动作用域,即使在 优化作用域 内运行也是如此。

该模块定义了以下函数;每个函数以略有不同的方式进入调试器:

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

在调试器控制下执行 statement(以字符串或代码对象形式给出)。调试器提示符会在任何代码执行之前出现;你可以设置断点并输入 continue,或者使用 stepnext 单步执行语句(所有这些命令将在下面解释)。可选的 globalslocals 参数指定了代码执行的环境;默认情况下,使用模块 __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 是调试器类。

completekeystdinstdout 参数被传递给底层的 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 模式(如果有的话)。

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 语句也可以用感叹号(!)作为前缀。这是检查被调试程序的一种强大方式;甚至可以更改变量或调用函数。当此类语句中发生异常时,会打印异常名称,但调试器的状态不会改变。

在 3.13 版更改: 前缀是 pdb 命令的表达式/语句现在可以被正确识别和执行。

调试器支持别名。别名可以有参数,这使得它在一定程度上能够适应被检查的上下文。

可以在单行中输入多个命令,用 ;; 分隔。(单个 ; 不被使用,因为它是传递给 Python 解析器的一行中多个命令的分隔符。)在分隔命令时没有应用任何智能;输入在第一个 ;; 对处被分割,即使它位于带引号的字符串中间。对于包含双分号的字符串,一个变通方法是使用隐式字符串拼接 ';'';'";"";"

要设置一个临时的全局变量,请使用*便捷变量*。*便捷变量*是一个名称以 $ 开头的变量。例如,$foo = 1 设置了一个全局变量 $foo,你可以在调试会话中使用它。当程序恢复执行时,*便捷变量*会被清除,因此与使用普通变量(如 foo = 1)相比,它不太可能干扰你的程序。

有四个预设的*便捷变量*:

  • $_frame:你正在调试的当前帧

  • $_retval:如果帧正在返回,则为返回值

  • $_exception:如果帧正在引发异常,则为该异常

  • $_asynctask:如果 pdb 在异步函数中停止,则为 asyncio 任务

3.12 版新加入: 添加了*便捷变量*功能。

3.14 版新加入: 添加了 $_asynctask 便捷变量。

如果用户的家目录或当前目录中存在文件 .pdbrc,它将以 'utf-8' 编码读取并执行,就像在调试器提示符下输入一样,但空行和以 # 开头的行将被忽略。这对别名特别有用。如果两个文件都存在,则首先读取家目录中的文件,并且其中定义的别名可以被本地文件覆盖。

在 3.2 版更改: .pdbrc 现在可以包含继续调试的命令,例如 continuenext。以前,这些命令没有效果。

在 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.pyrelpath/file.pymodulepackage.module

function 参数时,在该函数的第一个可执行语句处设置断点。function 可以是任何在当前命名空间中计算结果为函数的表达式。

如果存在第二个参数,它是一个表达式,必须评估为 true,断点才会被触发。

不带参数时,列出所有断点,包括每个断点被命中的次数、当前的忽略计数以及任何相关的条件。

每个断点都被分配一个编号,所有其他断点命令都通过这个编号来引用它。

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

临时断点,当它第一次被命中时会自动移除。参数与 break 相同。

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 或任何其他恢复执行的命令。

指定任何恢复执行的命令(目前是 continuestepnextreturnuntiljumpquit 及其缩写)会终止命令列表(就好像该命令后面立即跟了 end)。这是因为任何时候你恢复执行(即使是简单的 next 或 step),你都可能遇到另一个断点——它可能有自己的命令列表,导致关于执行哪个列表的歧义。

如果命令列表中包含 silent 命令,或者一个恢复执行的命令,那么包含帧信息的断点消息将不会显示。

在 3.14 版更改: 如果命令列表中存在恢复执行的命令,则不会显示帧信息。

s(tep)

执行当前行,在第一个可能的机会停下来(无论是在被调用的函数中,还是在当前函数的下一行)。

n(ext)

继续执行,直到到达当前函数的下一行或函数返回。(nextstep 的区别在于,step 会在被调用的函数内部停止,而 next 会以(几乎)全速执行被调用的函数,只在当前函数的下一行停止。)

unt(il) [lineno]

不带参数时,继续执行直到到达行号大于当前行的那一行。

lineno 时,继续执行直到到达行号大于或等于 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 命令,但 expression 的值会使用 pprint 模块进行美化打印。

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 的别名,用于执行 commandcommand 必须*不*用引号括起来。可替换的参数可以用 %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。历史、断点、动作和调试器选项都会被保留。restartrun 的一个别名。

在 3.14 版更改: 当调试器以 'inline' 模式调用时,runrestart 命令被禁用。

q(uit)

退出调试器。正在执行的程序将被中止。文件结束输入等同于 quit

如果调试器以 'inline' 模式调用,将显示一个确认提示。yY<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 版本加入。

脚注