27.3. pdb - Python调试器

源代码: Lib/pdb.py

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

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

调试器的提示符为(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文件中给出的一样,参见调试器命令

从正在运行的程序中断入调试器的典型用法是插入

import pdb; pdb.set_trace()

到你想要插入调试器的位置。然后,你可以按照逐步执行此语句后面的代码,并使用continue命令继续运行,而不使用调试器。

检查崩溃程序的典型用法是:

>>> import pdb
>>> import mymodule
>>> mymodule.test()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  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,也可以使用stepnext逐步执行该语句(所有这些命令将在下面说明)。可选的全局变量局部变量参数指定执行代码的环境;默认使用模块的字典__main__(请参阅内建exec()eval()函数的解释。)

pdb.runeval(expression, globals=None, locals=None)

在调试器控制下计算表达式(作为字符串或代码对象)。runeval()返回时,它返回表达式的值。否则,此函数类似于run()

pdb.runcall(function, *args, **kwds)

使用给定的参数调用函数(函数或方法对象,而不是字符串)。runcall()返回时,它返回所返回的任何函数调用。一旦输入该函数,就会显示调试器提示。

pdb.set_trace()

在调用堆栈框架处输入调试器。这对于在程序中的给定点硬编码断点是有用的,即使代码没有被另外调试(例如,当断言失败时)。

pdb.post_mortem(traceback=None)

输入给定traceback对象的事后调试。如果未给出traceback,则使用当前正在处理的异常(如果使用缺省值,则必须处理异常)。

pdb.pm()

输入在sys.last_traceback中找到的回溯的事后调试。

run*函数和set_trace()是用于实例化Pdb类和调用同名方法的别名。如果你想访问更多的功能,你必须自己这样做:

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

Pdb是调试器类。

completekeystdinstdout参数传递给底层的cmd.Cmd看到那里的描述。

如果给定了skip参数,则必须是可重复的glob样式模块名称模式。调试器不会步入到源于与这些模式之一匹配的模块中的帧。[1]

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

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

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

版本3.1中的新功能: 跳过参数。

版本3.2中的新功能: nosigint参数。以前,一个SIGINT处理程序从来没有被Pdb设置。

run(statement, globals=None, locals=None)
runeval(expression, globals=None, locals=None)
runcall(function, *args, **kwds)
set_trace()

请参阅上述功能的文档。

27.3.1. Debugger Commands

调试器识别的命令如下所示。大多数命令可以缩写为一个或两个字母,如图所示;例如h(elp) means that either h or help can be used to enter the help command (but not he or hel, nor H or Help or HELP). 命令的参数必须由空格(空格或制表符)分隔。可选参数在命令语法中用方括号([])括起来;不得输入方括号。命令语法中的其他选项通过垂直条(|)分隔。

输入空行重复输入的最后一个命令。异常:如果最后一个命令是list命令,则会列出接下来的11行。

调试器不能识别的命令假定为Python语句,并在正在调试的程序的上下文中执行。Python语句也可以带有感叹号(!)作为前缀。这是一种检查被调试程序的有效方法;甚至可以改变变量或调用函数。当在此类语句中发生异常时,将打印异常名称,但调试器的状态不会更改。

调试器支持aliases别名可以具有允许对检查的上下文具有一定水平的适应性的参数。

多个命令可以在单行上输入,由;;分隔。(不使用单个;,因为它是传递给Python解析器的行中的多个命令的分隔符。)没有智能用于分离命令;输入在第一个;;对处拆分,即使它位于带引号的字符串的中间。

如果文件.pdbrc存在于用户的主目录或当前目录中,则会读入该文件并执行,就好像它在调试器提示符处键入。这对别名特别有用。如果两个文件都存在,那么首先读取主目录中的一个文件,并在那里定义的别名可以被本地文件覆盖。

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

h(elp) [command]

无参数时,打印可用命令的列表。使用命令作为参数,打印有关该命令的帮助。help pdb显示完整文档(pdb模块的文档字符串)。Since the command argument must be an identifier, help exec must be entered to get help on the !命令。

w(here)

打印堆栈跟踪,最新的帧在底部。箭头表示当前帧,它确定大多数命令的上下文。

d(own) [count]

将当前帧计数(默认一个)向下移动到堆栈跟踪(更新的帧)。

u(p) [count]

将当前帧计数(默认值)移动到堆栈跟踪(到较早的帧)。

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

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

如果存在第二个参数,它是一个表达式,在断点被执行之前必须计算为true。

无参数,列出所有中断,包括每个断点,断点被命中的次数,当前忽略计数和相关条件(如果有)。

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

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

cl(ear) [filename:lineno | bpnumber [bpnumber ...]]

使用filename:lineno参数,清除此行的所有断点。用断点号的空格分隔列表,清除那些断点。没有论证,清除所有休息(但首先要求确认)。

disable [bpnumber [bpnumber ...]]

禁用以空格分隔的断点号列表形式给出的断点。禁用断点意​​味着它不能导致程序停止执行,但是与清除断点不同,它保留在断点列表中,并且可以(重新)启用。

enable [bpnumber [bpnumber ...]]

启用指定的断点。

ignore bpnumber [count]

设置给定断点号的忽略计数。如果省略count,则忽略计数设置为0。当忽略计数为零时,断点变为活动状态。当非零时,每次达到断点时计数递减,并且不禁用断点,并且任何关联条件的计算结果为true。

condition bpnumber [condition]

为断点设置新的条件,这是在断点被执行之前必须计算为true的表达式。如果条件不存在,则删除任何现有条件;即断点是无条件的。

commands [bpnumber]

指定断点号bpnumber的命令列表。命令本身显示在以下行。键入仅包含end的行以终止命令。一个例子:

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

要从断点中删除所有命令,请键入命令,并立即使用end;即,不给出命令。

没有bpnumber参数,命令引用最后一个断点集。

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

指定任何恢复执行的命令(当前继续,步骤,下一步,返回,跳转,退出和它们的缩写)终止命令列表(就好像该命令紧接着结束)。这是因为任何时候你恢复执行(即使有一个简单的下一步或步骤),你可能会遇到另一个断点,这可能有自己的命令列表,导致执行列表的模糊。

如果在命令列表中使用“silent”命令,则不会打印关于在断点处停止的常见消息。这对于打印特定消息然后继续的断点可能是合乎需要的。如果没有其他命令打印任何东西,您看不到到达断点的迹象。

s(tep)

执行当前行,在第一个可能的时间停止(在调用的函数中或当前函数中的下一行)。

n(ext)

继续执行直到到达当前函数中的下一行或返回。nextstep之间的区别是step在被调用函数内停止,而next (几乎)全速,只在当前功能的下一行停止。)

unt(il) [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

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

注意

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

pp expression

p命令,除了表达式的值是使用pprint模块打印的。

whatis expression

打印表达式的类型。

source expression

尝试获取给定对象的源代码并显示它。

版本3.2中的新功能。

display [expression]

如果表达式更改,则每次在当前帧中停止执行时,显示表达式的值。

不带表达式时,列出当前帧的所有显示表达式。

版本3.2中的新功能。

undisplay [expression]

不要在当前框架中再显示表达式。如果没有表达式,请清除当前框架的所有显示表达式。

版本3.2中的新功能。

interact

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

版本3.2中的新功能。

alias [name [command]]

创建一个名为name的执行​​命令的别名。The command must not be enclosed in quotes. 可替换参数可由%1%2等指示,而%*由所有参数替换。如果未给出命令,则会显示名称的当前别名。如果没有给出参数,则列出所有别名。

别名可以嵌套,并且可以包含可以在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
unalias name

删除指定的别名。

statement

在当前堆栈帧的上下文中执行(一行)语句可以省略感叹号,除非语句的第一个单词类似于调试器命令。要设置全局变量,您可以在同一行上使用global语句为赋值命令添加前缀,例如。

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

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

q(uit)

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

脚注

[1]帧是否被认为源于某个模块由帧全局中的__name__确定。