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 myscript.py

当作为模块调用时,如果被调试的程序异常退出,pdb 将自动进入事后调试。在事后调试之后(或程序正常退出之后),pdb 将重新启动程序。自动重新启动会保留 pdb 的状态(例如断点),并且在大多数情况下比在程序退出时退出调试器更有用。

在 3.2 版本中变更: 添加了 -c 选项,用于执行命令,就像在 .pdbrc 文件中给出的一样;请参阅 调试器命令

在 3.7 版本中变更: 添加了 -m 选项,用于执行模块,类似于 python -m 的方式。与脚本一样,调试器将在模块第一行代码之前暂停执行。

在调试器控制下执行语句的典型用法是

>>> 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 单步执行语句(所有这些命令在下面进行解释)。可选的 *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)

在调用堆栈帧处进入调试器。这对于在程序的给定点硬编码断点非常有用,即使代码没有进行调试(例如,当断言失败时)。如果给定header,则在调试开始之前将其打印到控制台。

在版本 3.7 中更改: 仅限关键字参数 header

在版本 3.13 中更改: set_trace() 将立即进入调试器,而不是在要执行的下一行代码处进入。

pdb.post_mortem(traceback=None)

进入给定 traceback 对象的事后调试。如果没有给出 traceback,它将使用当前正在处理的异常的 traceback(如果要使用默认值,则必须正在处理异常)。

pdb.pm()

进入在 sys.last_exc 中找到的异常的事后调试。

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 风格的模块名称模式的可迭代对象。调试器不会步入源自匹配其中一种模式的模块的帧。[1]

默认情况下,当你给出 continue 命令时,Pdb 会为 SIGINT 信号设置一个处理程序(当用户在控制台上按下 Ctrl-C 时发送)。这允许你通过按下 Ctrl-C 再次进入调试器。如果你希望 Pdb 不触及 SIGINT 处理程序,请将 nosigint 设置为 true。

readrc 参数默认为 true,并控制 Pdb 是否从文件系统加载 .pdbrc 文件。

使用 skip 启用跟踪的示例调用

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

引发一个没有参数的 审计事件 pdb.Pdb

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

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

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

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

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

有三个预设的便利变量

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

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

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

在版本 3.12 中添加: 添加了便利变量功能。

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

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

3.11 版本更改: .pdbrc 现在以 'utf-8' 编码读取。 以前,它使用系统区域设置编码读取。

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 参数,在当前文件的 lineno 行设置断点。 行号可以带一个 filename 和一个冒号作为前缀,以指定另一个文件中的断点 (可能尚未加载)。 该文件会在 sys.path 中搜索。 filename 的可接受形式为 /abspath/to/file.py, relpath/file.py, modulepackage.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。 当忽略计数为零时,断点变为活动状态。 当非零时,每次到达断点时,count 都会递减,并且断点未禁用,并且任何关联的条件都求值为 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 指的是最后设置的断点。

您可以使用断点命令来再次启动程序。 只需使用 continue 命令,或 step 或任何其他恢复执行的命令。

指定任何恢复执行的命令 (当前为 continue, step, next, return, jump, quit 及其缩写) 会终止命令列表 (就好像该命令紧接着是 end)。 这是因为任何时候你恢复执行 (即使使用简单的 next 或 step),你都可能会遇到另一个断点 —— 它可能也有自己的命令列表,导致关于要执行哪个列表的歧义。

如果在命令列表中使用 silent 命令,则不会打印关于在断点处停止的通常消息。 对于要打印特定消息然后继续的断点,这可能是可取的。 如果没有其他命令打印任何内容,您将看不到断点已到达的任何迹象。

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 表达式

在当前上下文中计算表达式并打印其值。

注意

print() 也可以使用,但它不是调试器命令 — 它执行 Python print() 函数。

pp 表达式

p 命令类似,只是 表达式 的值使用 pprint 模块进行美化打印。

whatis 表达式

打印表达式的类型。

source 表达式

尝试获取表达式的源代码并显示它。

在 3.2 版本中添加。

display [表达式]

如果表达式的值发生变化,则每次执行在当前帧中停止时都显示该值。

如果没有 表达式,则列出当前帧的所有显示表达式。

注意

Display 会计算表达式,并与之前对表达式的评估结果进行比较,因此当结果是可变的,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 [表达式]

不再在当前帧中显示表达式。如果没有 表达式,则清除当前帧的所有显示表达式。

在 3.2 版本中添加。

interact

在从当前作用域的局部和全局命名空间初始化的新全局命名空间中启动一个交互式解释器 (使用 code 模块)。使用 exit()quit() 退出解释器并返回到调试器。

注意

由于 interact 为代码执行创建了一个新的专用命名空间,因此对变量的赋值不会影响原始命名空间。但是,对任何引用的可变对象的修改将像往常一样反映在原始命名空间中。

在 3.2 版本中添加。

在 3.13 版本中更改: exit()quit() 可用于退出 interact 命令。

在 3.13 版本中更改: interact 将其输出定向到调试器的输出通道,而不是 sys.stderr

alias [名称 [命令]]

创建一个名为 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

! 语句

在当前堆栈帧的上下文中执行(单行)语句。 感叹号可以省略,除非语句的第一个单词类似于调试器命令,例如

(Pdb) ! n=42
(Pdb)

要设置一个全局变量,你可以在同一行上的赋值命令前加上 global 语句,例如

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

重新启动被调试的 Python 程序。 如果提供了 args,则会使用 shlex 进行分割,结果将用作新的 sys.argv。 历史记录、断点、操作和调试器选项都将保留。 restartrun 的别名。

q(uit)

退出调试器。正在执行的程序被中止。

debug 代码

输入一个递归调试器,该调试器会单步执行代码(它是要在当前环境中执行的任意表达式或语句)。

retval

打印当前函数最后一次返回的返回值。

exceptions [excnumber]

列出或在链接的异常之间跳转。

当使用 pdb.pm()Pdb.post_mortem(...) 以及链接的异常而不是回溯时,它允许用户使用 exceptions 命令列出异常,并使用 exception <数字> 切换到该异常,从而在链接的异常之间移动。

示例

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 版本中添加。

脚注