sys.monitoring — 执行事件监控

在 3.12 版本中新增。


注解

sys.monitoringsys 模块中的一个命名空间,而不是一个独立的模块,因此无需 import sys.monitoring,只需 import sys 然后使用 sys.monitoring 即可。

此命名空间提供对激活和控制事件监控所需的函数和常量的访问。

当程序执行时,会发生一些工具可能感兴趣的事件,这些工具可以监控执行。sys.monitoring 命名空间提供了在发生感兴趣的事件时接收回调的方法。

监控 API 由三个组件组成

工具标识符

工具标识符是一个整数和关联的名称。工具标识符用于防止工具相互干扰,并允许多个工具同时运行。目前,工具是完全独立的,不能用于互相监控。未来可能会取消此限制。

在注册或激活事件之前,工具应选择一个标识符。标识符是 0 到 5 之间的整数(包括 0 和 5)。

注册和使用工具

sys.monitoring.use_tool_id(tool_id: int, name: str, /) None

必须在 tool_id 可以使用之前调用。tool_id 必须在 0 到 5 的范围内(包括 0 和 5)。如果 tool_id 正在使用,则会引发 ValueError

sys.monitoring.free_tool_id(tool_id: int, /) None

当工具不再需要 tool_id 时应调用此方法。

注解

free_tool_id() 不会禁用与 tool_id 关联的全局或本地事件,也不会取消注册任何回调函数。此函数仅用于通知 VM,特定的 tool_id 不再使用。

sys.monitoring.get_tool(tool_id: int, /) str | None

如果 tool_id 正在使用,则返回工具的名称,否则返回 Nonetool_id 必须在 0 到 5 的范围内(包括 0 和 5)。

所有 ID 在 VM 中都以相同的方式处理事件,但以下 ID 是预定义的,以便更容易进行工具协作

sys.monitoring.DEBUGGER_ID = 0
sys.monitoring.COVERAGE_ID = 1
sys.monitoring.PROFILER_ID = 2
sys.monitoring.OPTIMIZER_ID = 5

事件

支持以下事件

sys.monitoring.events.BRANCH

发生条件分支(被执行或不被执行)。

sys.monitoring.events.CALL

在 Python 代码中发生调用(事件发生在调用之前)。

sys.monitoring.events.C_RAISE

从任何可调用对象引发异常,Python 函数除外(事件发生在退出后)。

sys.monitoring.events.C_RETURN

从任何可调用对象返回,Python 函数除外(事件发生在返回后)。

sys.monitoring.events.EXCEPTION_HANDLED

异常被处理。

sys.monitoring.events.INSTRUCTION

即将执行 VM 指令。

sys.monitoring.events.JUMP

在控制流图中发生无条件跳转。

sys.monitoring.events.LINE

即将执行的指令与前一条指令的行号不同。

sys.monitoring.events.PY_RESUME

恢复 Python 函数(用于生成器和协程函数),throw() 调用除外。

sys.monitoring.events.PY_RETURN

从 Python 函数返回(发生在返回之前,被调用者的帧将在堆栈上)。

sys.monitoring.events.PY_START

Python 函数的开始(发生在调用之后,被调用者的帧将在堆栈上)

sys.monitoring.events.PY_THROW

通过 throw() 调用恢复 Python 函数。

sys.monitoring.events.PY_UNWIND

在异常展开期间从 Python 函数退出。

sys.monitoring.events.PY_YIELD

从 Python 函数产生值(发生在 yield 之前,被调用者的帧将在堆栈上)。

sys.monitoring.events.RAISE

引发异常,除了那些导致 STOP_ITERATION 事件的异常。

sys.monitoring.events.RERAISE

重新引发异常,例如在 finally 代码块的末尾。

sys.monitoring.events.STOP_ITERATION

引发一个人为的 StopIteration 异常;请参阅 STOP_ITERATION 事件

未来可能会添加更多事件。

这些事件是 sys.monitoring.events 命名空间的属性。每个事件都表示为一个 2 的幂的整数常量。要定义一组事件,只需将各个事件按位或运算即可。例如,要指定 PY_RETURNPY_START 事件,请使用表达式 PY_RETURN | PY_START

sys.monitoring.events.NO_EVENTS

0 的别名,以便用户可以进行显式比较,例如

if get_events(DEBUGGER_ID) == NO_EVENTS:
    ...

事件分为三组

本地事件

本地事件与程序的正常执行相关联,并且发生在明确定义的位置。所有本地事件都可以禁用。本地事件有

辅助事件

辅助事件可以像其他事件一样进行监视,但由另一个事件控制

C_RETURNC_RAISE 事件由 CALL 事件控制。 只有在监视相应的 CALL 事件时,才会看到 C_RETURNC_RAISE 事件。

其他事件

其他事件不一定与程序中的特定位置相关联,并且不能单独禁用。

可以监视的其他事件有

STOP_ITERATION 事件

PEP 380 指定,当从生成器或协程返回值时,会引发 StopIteration 异常。但是,这是一种非常低效的返回值方式,因此一些 Python 实现(特别是 CPython 3.12+)不会引发异常,除非它对其他代码可见。

为了允许工具在不减慢生成器和协程速度的情况下监视真实异常,提供了 STOP_ITERATION 事件。与 RAISE 不同,STOP_ITERATION 可以在本地禁用。

启用和关闭事件

为了监视事件,必须启用它并注册相应的回调。可以通过全局设置或为特定代码对象设置事件来启用或关闭事件。

全局设置事件

可以通过修改正在监视的事件集来全局控制事件。

sys.monitoring.get_events(tool_id: int, /) int

返回表示所有活动事件的 int

sys.monitoring.set_events(tool_id: int, event_set: int, /) None

激活 event_set 中设置的所有事件。如果 tool_id 未使用,则引发 ValueError

默认情况下,没有事件处于活动状态。

每个代码对象事件

也可以在每个代码对象的基础上控制事件。下面定义的接受 types.CodeType 的函数应该准备好接受来自未在 Python 中定义的函数的类似对象(请参阅 Monitoring C API)。

sys.monitoring.get_local_events(tool_id: int, code: CodeType, /) int

返回 code 的所有本地事件

sys.monitoring.set_local_events(tool_id: int, code: CodeType, event_set: int, /) None

激活 codeevent_set 中设置的所有本地事件。如果 tool_id 未使用,则引发 ValueError

本地事件会添加到全局事件中,但不会屏蔽全局事件。换句话说,无论本地事件如何,所有全局事件都将为代码对象触发。

禁用事件

sys.monitoring.DISABLE

一个特殊值,可以从回调函数返回,以禁用当前代码位置的事件。

可以通过从回调函数返回 sys.monitoring.DISABLE 来禁用特定代码位置的本地事件。这不会更改设置的事件或同一事件的任何其他代码位置。

禁用特定位置的事件对于高性能监视非常重要。例如,如果调试器禁用除几个断点之外的所有监视,则程序可以在没有开销的情况下在调试器下运行。

sys.monitoring.restart_events() None

启用所有被 sys.monitoring.DISABLE 禁用的所有工具的事件。

注册回调函数

要为事件注册可调用对象,请调用

sys.monitoring.register_callback(tool_id: int, event: int, func: Callable | None, /) Callable | None

为给定的 tool_id 注册用于 event 的可调用对象 func

如果为给定的 tool_idevent 注册了另一个回调,则会取消注册并返回该回调。否则,register_callback() 返回 None

可以通过调用 sys.monitoring.register_callback(tool_id, event, None) 来取消注册函数。

回调函数可以随时注册和取消注册。

注册或取消注册回调函数将生成 sys.audit() 事件。

回调函数参数

sys.monitoring.MISSING

一个特殊的值,传递给回调函数,表示该调用没有参数。

当活动事件发生时,将调用已注册的回调函数。不同的事件将为回调函数提供不同的参数,如下所示

  • PY_STARTPY_RESUME

    func(code: CodeType, instruction_offset: int) -> DISABLE | Any
    
  • PY_RETURNPY_YIELD

    func(code: CodeType, instruction_offset: int, retval: object) -> DISABLE | Any
    
  • CALL, C_RAISEC_RETURN

    func(code: CodeType, instruction_offset: int, callable: object, arg0: object | MISSING) -> DISABLE | Any
    

    如果没有参数,则 arg0 设置为 sys.monitoring.MISSING

  • RAISE, RERAISE, EXCEPTION_HANDLED, PY_UNWIND, PY_THROWSTOP_ITERATION

    func(code: CodeType, instruction_offset: int, exception: BaseException) -> DISABLE | Any
    
  • LINE:

    func(code: CodeType, line_number: int) -> DISABLE | Any
    
  • BRANCHJUMP

    func(code: CodeType, instruction_offset: int, destination_offset: int) -> DISABLE | Any
    

    请注意,destination_offset 是代码接下来将执行的位置。对于未采用的分支,这将是分支后指令的偏移量。

  • INSTRUCTION:

    func(code: CodeType, instruction_offset: int) -> DISABLE | Any