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),它表示您处于调试模式

> ...(3)double()
-> return x * 2
(Pdb) p x
3
(Pdb) continue
3 * 2 is 6

版本 3.3 中的变更: 通过 readline 模块提供的制表符补全可用于命令和命令参数,例如,当前的全局和局部名称将作为 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)

该模块定义了以下函数;每个函数都以稍微不同的方式进入调试器

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)

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

版本 3.7 中的变更: 仅限关键字的参数 header

pdb.post_mortem(traceback=None)

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

pdb.pm()

进入在sys.last_traceback中找到的traceback的后期调试。

The run* functions and set_trace() are aliases for instantiating the Pdb class and calling the method of the same name. If you want to access further features, you have to do this yourself

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

Pdb 是调试器类。

The completekey, stdin and stdout arguments are passed to the underlying cmd.Cmd class; see the description there.

如果给出skip参数,它必须是 glob 样式模块名称模式的可迭代对象。调试器不会进入源自与这些模式之一匹配的模块的帧。 [1]

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

The readrc argument defaults to true and controls whether Pdb will load .pdbrc files from the filesystem.

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

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

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

在版本 3.1 中更改: 添加了skip参数。

在版本 3.2 中更改: 添加了nosigint参数。以前,Pdb 从未设置 SIGINT 处理程序。

在版本 3.6 中更改: The readrc argument.

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 解析器的行中用于分隔多条命令的分隔符。)不会对分隔命令应用任何智能;输入在第一个 ;; 对处分割,即使它在引号字符串的中间。对于包含双分号的字符串,一种解决方法是使用隐式字符串连接 ';'';'";"";"

要设置一个临时的全局变量,请使用便利变量便利变量是一个名称以 $ 开头的变量。例如,$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(默认值为一个)级(到一个较新的帧)。

u(p) [count]

在堆栈跟踪中将当前帧向上移动count(默认值为一个)级(到一个较旧的帧)。

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

使用lineno参数,在当前文件中设置断点。使用function参数,在该函数中的第一个可执行语句处设置断点。行号可以以文件名和冒号为前缀,以指定另一个文件中的断点(可能是尚未加载的文件)。在 sys.path 上搜索文件。请注意,每个断点都分配了一个数字,所有其他断点命令都引用该数字。

如果存在第二个参数,则它是一个表达式,该表达式必须在断点生效之前计算为真。

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

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 都会递减,并且断点不会被禁用,并且任何关联的条件都会计算为真。

condition bpnumber [condition]

为断点设置一个新的condition,一个表达式,该表达式必须在断点生效之前计算为真。如果condition不存在,则会删除任何现有的条件;即断点变为无条件的。

commands [bpnumber]

为断点编号bpnumber指定命令列表。命令本身出现在接下来的行中。键入仅包含 end 的一行以终止命令。一个例子

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

要从断点中删除所有命令,请键入 commands,然后立即键入 end;也就是说,不给出任何命令。

如果没有bpnumber参数,则 commands 指的是最后设置的断点。

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

指定任何恢复执行的命令(目前包括 continuestepnextreturnjumpquit 以及它们的缩写)都会终止命令列表(就像该命令后面紧跟着 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 expression

在当前上下文中评估 expression 并打印其值。

注意

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

pp expression

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

whatis expression

打印 expression 的类型。

source expression

尝试获取 expression 的源代码并显示它。

在版本 3.2 中添加。

display [expression]

每次在当前帧中停止执行时,如果 expression 的值发生了变化,则显示它的值。

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

注意

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,清除当前帧的所有显示表达式。

在版本 3.2 中添加。

interact

启动一个交互式解释器(使用 code 模块),其全局命名空间包含在当前作用域中找到的所有(全局和局部)名称。

在版本 3.2 中添加。

alias [name [command]]

创建一个名为 name 的别名,它执行 commandcommand 不能用引号括起来。可替换的参数可以用 %1%2 等表示,而 %* 被所有参数替换。如果省略 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 的别名。

q(uit)

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

debug code

进入递归调试器,它将逐步执行 *code*(它是在当前环境中执行的任意表达式或语句)。

retval

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

脚注