bdb --- 调试器框架

源代码: Lib/bdb.py


bdb 模块处理基本的调试器功能,例如设置断点或通过调试器管理执行。

定义了以下异常:

exception bdb.BdbQuit

Bdb 类引发的异常,用于退出调试器。

bdb 模块还定义了两个类:

class bdb.Breakpoint(self, file, line, temporary=False, cond=None, funcname=None)

这个类实现了临时断点、忽略次数、禁用和(重新)启用以及条件断点。

断点通过名为 bpbynumber 的列表按编号索引,并通过 (file, line) 对通过 bplist 索引。前者指向 Breakpoint 类的单个实例。后者指向一个这类实例的列表,因为每行可能有多个断点。

创建断点时,其关联的 文件名 应为规范形式。如果定义了 funcname,则当该函数的第一行被执行时,断点 命中 将被计数。一个 条件 断点总是会计算一次 命中

Breakpoint 实例具有以下方法:

deleteMe()

从与文件/行关联的列表中删除断点。如果它是该位置的最后一个断点,它还会删除文件/行的条目。

enable()

将断点标记为启用。

disable()

将断点标记为禁用。

bpformat()

返回一个包含所有关于断点信息的字符串,并进行良好格式化:

  • 断点编号。

  • 临时状态(del 或 keep)。

  • 文件/行位置。

  • 中断条件。

  • 要忽略的次数。

  • 命中次数。

在 3.2 版本加入。

bpprint(out=None)

bpformat() 的输出打印到文件 *out*,如果 *out* 为 None,则打印到标准输出。

Breakpoint 实例具有以下属性:

file

Breakpoint 的文件名。

line

fileBreakpoint 的行号。

temporary

如果位于(文件,行)的 Breakpoint 是临时的,则为 True

cond

评估位于(文件,行)的 Breakpoint 的条件。

funcname

函数名,用于定义进入该函数时是否命中 Breakpoint

enabled

如果 Breakpoint 已启用,则为 True

bpbynumber

单个 Breakpoint 实例的数字索引。

bplist

以 (file, line) 元组为索引的 Breakpoint 实例的字典。

ignore

忽略一个 Breakpoint 的次数。

hits

一个 Breakpoint 被命中的次数计数。

class bdb.Bdb(skip=None, backend='settrace')

Bdb 类作为一个通用的 Python 调试器基类。

这个类负责处理跟踪工具的细节;派生类应该实现用户交互。标准调试器类 (pdb.Pdb) 就是一个例子。

如果提供了 *skip* 参数,它必须是一个 glob 风格模块名模式的可迭代对象。调试器将不会进入源自于匹配这些模式之一的模块的帧。一个帧是否被认为源自某个特定模块是由该帧的全局变量中的 __name__ 决定的。

*backend* 参数指定用于 Bdb 的后端。它可以是 'settrace''monitoring''settrace' 使用 sys.settrace(),它具有最好的向后兼容性。 'monitoring' 后端使用 Python 3.12 中引入的新 sys.monitoring,它可以更高效,因为它能禁用未使用的事件。我们试图为两个后端保持完全相同的接口,但存在一些差异。鼓励调试器开发者使用 'monitoring' 后端以获得更好的性能。

在 3.1 版本发生变更: 添加了 *skip* 形参。

在 3.14 版本发生变更: 添加了 *backend* 形参。

以下 Bdb 的方法通常不需要被重写。

canonic(filename)

返回 *filename* 的规范形式。

对于真实的文件名,规范形式是一个依赖于操作系统、大小写规范化绝对 路径。一个带有尖括号的 *filename*,例如在交互模式下生成的 "<stdin>",会原样返回。

start_trace(self)

开始跟踪。对于 'settrace' 后端,此方法等同于 sys.settrace(self.trace_dispatch)

在 3.14 版本加入。

stop_trace(self)

停止跟踪。对于 'settrace' 后端,此方法等同于 sys.settrace(None)

在 3.14 版本加入。

reset()

设置 botframe, stopframe, returnframequitting 属性,其值准备好开始调试。

trace_dispatch(frame, event, arg)

此函数作为被调试帧的跟踪函数被安装。它的返回值是新的跟踪函数(在大多数情况下,就是它本身)。

默认实现根据即将执行的事件类型(作为字符串传递)来决定如何分派一个帧。*event* 可以是以下之一:

  • "line": 即将执行一行新代码。

  • "call": 即将调用一个函数,或进入另一个代码块。

  • "return": 一个函数或其他代码块即将返回。

  • "exception": 发生了一个异常。

  • "c_call": 即将调用一个 C 函数。

  • "c_return": 一个 C 函数已返回。

  • "c_exception": 一个 C 函数引发了异常。

对于 Python 事件,会调用专门的函数(见下文)。对于 C 事件,不采取任何操作。

*arg* 参数取决于前一个事件。

有关跟踪函数的更多信息,请参阅 sys.settrace() 的文档。有关代码和帧对象的更多信息,请参阅 标准类型层级结构

dispatch_line(frame)

如果调试器应该在当前行停止,则调用 user_line() 方法(应在子类中重写)。如果 quitting 标志被设置(可以从 user_line() 中设置),则引发 BdbQuit 异常。返回对 trace_dispatch() 方法的引用,以便在该作用域中进行进一步的跟踪。

dispatch_call(frame, arg)

如果调试器应该在此函数调用处停止,则调用 user_call() 方法(应在子类中重写)。如果 quitting 标志被设置(可以从 user_call() 中设置),则引发 BdbQuit 异常。返回对 trace_dispatch() 方法的引用,以便在该作用域中进行进一步的跟踪。

dispatch_return(frame, arg)

如果调试器应该在此函数返回处停止,则调用 user_return() 方法(应在子类中重写)。如果 quitting 标志被设置(可以从 user_return() 中设置),则引发 BdbQuit 异常。返回对 trace_dispatch() 方法的引用,以便在该作用域中进行进一步的跟踪。

dispatch_exception(frame, arg)

如果调试器应该在此异常处停止,则调用 user_exception() 方法(应在子类中重写)。如果 quitting 标志被设置(可以从 user_exception() 中设置),则引发 BdbQuit 异常。返回对 trace_dispatch() 方法的引用,以便在该作用域中进行进一步的跟踪。

通常派生类不会重写以下方法,但如果它们想重新定义停止和断点的定义,则可以这样做。

is_skipped_line(module_name)

如果 *module_name* 匹配任何跳过模式,则返回 True

stop_here(frame)

如果 *frame* 在堆栈中低于起始帧,则返回 True

break_here(frame)

如果此行存在有效断点,则返回 True

检查是否存在行或函数断点并且生效。根据来自 effective() 的信息删除临时断点。

break_anywhere(frame)

如果 *frame* 的文件名存在任何断点,则返回 True

派生类应重写这些方法以控制调试器操作。

user_call(frame, argument_list)

如果中断可能在被调用函数内部停止,则从 dispatch_call() 中调用。

*argument_list* 不再使用,将始终为 None。保留此参数是为了向后兼容。

user_line(frame)

stop_here()break_here() 返回 True 时,从 dispatch_line() 中调用。

user_return(frame, return_value)

stop_here() 返回 True 时,从 dispatch_return() 中调用。

user_exception(frame, exc_info)

stop_here() 返回 True 时,从 dispatch_exception() 中调用。

do_clear(arg)

处理当一个断点是临时断点时,必须如何移除它。

此方法必须由派生类实现。

派生类和客户端可以调用以下方法来影响单步执行状态。

set_step()

在执行一行代码后停止。

set_next(frame)

在给定帧之内或之下的下一行停止。

set_return(frame)

从给定帧返回时停止。

set_until(frame, lineno=None)

当达到行号大于当前行的行,或从当前帧返回时停止。

set_trace([frame])

从 *frame* 开始调试。如果未指定 *frame*,则从调用者的帧开始调试。

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

set_continue()

仅在断点处或完成时停止。如果没有断点,将系统跟踪函数设置为 None

set_quit()

quitting 属性设置为 True。这会在下一次调用 dispatch_*() 方法之一时引发 BdbQuit

派生类和客户端可以调用以下方法来操作断点。如果出现问题,这些方法返回包含错误消息的字符串;如果一切正常,则返回 None

set_break(filename, lineno, temporary=False, cond=None, funcname=None)

设置一个新断点。如果作为参数传递的 *filename* 中不存在 *lineno* 行,则返回错误消息。*filename* 应为规范形式,如 canonic() 方法中所述。

clear_break(filename, lineno)

删除 *filename* 和 *lineno* 中的断点。如果未设置任何断点,则返回错误消息。

clear_bpbynumber(arg)

删除在 Breakpoint.bpbynumber 中索引为 *arg* 的断点。如果 *arg* 不是数字或超出范围,则返回错误消息。

clear_all_file_breaks(filename)

删除 *filename* 中的所有断点。如果未设置任何断点,则返回错误消息。

clear_all_breaks()

删除所有现有的断点。如果没有设置断点,则返回错误消息。

get_bpbynumber(arg)

返回由给定编号指定的断点。如果 *arg* 是字符串,它将被转换为数字。如果 *arg* 是非数字字符串,或者给定的断点从未存在或已被删除,则引发 ValueError

在 3.2 版本加入。

get_break(filename, lineno)

如果在 *filename* 的 *lineno* 处存在断点,则返回 True

get_breaks(filename, lineno)

返回 *filename* 中 *lineno* 处的所有断点,如果未设置则返回空列表。

get_file_breaks(filename)

返回 *filename* 中的所有断点,如果未设置则返回空列表。

get_all_breaks()

返回所有已设置的断点。

派生类和客户端可以调用以下方法来禁用和重启事件,以获得更好的性能。这些方法仅在使用 'monitoring' 后端时有效。

disable_current_event()

禁用当前事件,直到下次调用 restart_events()。当调试器对当前行不感兴趣时,这很有用。

在 3.14 版本加入。

restart_events()

重新启动所有已禁用的事件。此函数在调用 user_* 方法之后,会在 dispatch_* 方法中自动调用。如果 dispatch_* 方法没有被重写,已禁用的事件将在每次用户交互后重新启动。

在 3.14 版本加入。

派生类和客户端可以调用以下方法来获取表示堆栈跟踪的数据结构。

get_stack(f, t)

返回堆栈跟踪中的 (frame, lineno) 元组列表和一个大小。

最近调用的帧在列表的最后。大小是调试器被调用处的帧以下的帧数。

format_stack_entry(frame_lineno, lprefix=': ')

返回一个包含堆栈条目信息的字符串,堆栈条目是一个 (frame, lineno) 元组。返回的字符串包含:

  • 包含该帧的规范文件名。

  • 函数名或 "<lambda>"

  • 输入参数。

  • 返回值。

  • 代码行(如果存在)。

以下两个方法可以由客户端调用,以使用调试器来调试一个以字符串形式给出的 语句

run(cmd, globals=None, locals=None)

通过 exec() 函数调试一个语句。*globals* 默认为 __main__.__dict__,*locals* 默认为 *globals*。

runeval(expr, globals=None, locals=None)

通过 eval() 函数调试一个表达式。*globals* 和 *locals* 的含义与 run() 中的相同。

runctx(cmd, globals, locals)

为了向后兼容。调用 run() 方法。

runcall(func, /, *args, **kwds)

调试单个函数调用,并返回其结果。

最后,该模块定义了以下函数:

bdb.checkfuncname(b, frame)

根据 Breakpoint *b* 的设置方式,返回是否应该在此处中断。

如果它是通过行号设置的,它会检查 b.line 是否与 *frame* 中的行号相同。如果断点是通过 函数名 设置的,我们必须检查我们是否在正确的 *frame*(正确的函数)中,以及我们是否在其第一个可执行行上。

bdb.effective(file, line, frame)

返回 (活动断点, 删除临时标志)(None, None) 作为要操作的断点。

*活动断点*是 bplist 中针对 (file, line)(必须存在)的第一个条目,该条目是 enabled 的,checkfuncname() 对其为真,并且既没有为假的 条件,也没有正的 ignore 计数。表示应删除临时断点的*标志*仅在无法评估 cond 时为 False(在这种情况下,ignore 计数被忽略)。

如果不存在这样的条目,则返回 (None, None)

bdb.set_trace()

从调用者的帧开始,使用一个 Bdb 实例进行调试。