sys.monitoring
--- 执行事件监测¶
Added in version 3.12.
备注
sys.monitoring
是 sys
模块内部的一个命名空间,而不是一个独立模块,因此不需要 import sys.monitoring
,只要简单地 import sys
然后使用 sys.monitoring
。
这个命名空间提供了对于激活和控制事件监控所需的函数和常量的访问。
在程序执行过程中,会发生对于监控执行的工具来说值得关注的事件。 sys.monitoring
命名空间提供了在相应事件发生时接收回调的操作方式。
monitoring API由三个部分组成:
工具标识符¶
工具标识符是一个整数及其所关联的名称。 工具标识符被用来防止工具之间的相互干扰并允许同时操作多个工作。 目前工具是完全独立的且不能被用于相互监控。 这一限制在将来可能会被取消。
在注册或激活事件之前,工具应选择一个标识符。 标识符是 0 到 5 的开区间内的整数。
注册和使用工具¶
- sys.monitoring.use_tool_id(tool_id: int, name: str, /) None ¶
必须在 tool_id 可被使用之前调用。 tool_id 必须在 0 到 5 的开区间内。 如果 tool_id 已被使用则会引发
ValueError
。
- sys.monitoring.free_tool_id(tool_id: int, /) None ¶
应该在工具不再需要 tool_id 时调用。 在释放 tool_id 之前将调用
clear_tool_id()
。
- sys.monitoring.get_tool(tool_id: int, /) str | None ¶
如果 tool_id 已被使用则返回工具名称,否则返回
None
。 tool_id 取值必须在 0 至 5 的开区间内。
虚拟机在处理事件时对所有 ID 都一视同仁,但为便于工具之间的协作而预定义了下列 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_LEFT¶
条件分支向左。
由该工具决定如何表示“左”和“右”分支。不能保证哪个分支是“左”哪个分支是“右”,除非它在程序的持续时间内是一致的。
- sys.monitoring.events.BRANCH_RIGHT¶
条件分支向右。
- 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¶
一个 Python 函数由
throw()
调用恢复执行。
- sys.monitoring.events.PY_UNWIND¶
在异常展开期间从一个 Python 函数退出。 这包括在该函数内直接引发的以及被允许继续传播的异常。
- sys.monitoring.events.PY_YIELD¶
从一个 Python 函数产出数据(在产出之前立即发生,被调用方的帧将在栈中)。
- sys.monitoring.events.RAISE¶
一个异常被引发,导致
STOP_ITERATION
事件的异常除外。
- sys.monitoring.events.STOP_ITERATION¶
一个
StopIteration
被人工引发;参见 the STOP_ITERATION event。
将来可能会添加更多事件。
这些事件都是 sys.monitoring.events
命名空间的属性。 每个事件用整数常量的 2 次幂来表示。 要定义一组事件,只需对多个单独事件执行按位或运算即可。 例如,要同时指定 PY_RETURN
和 PY_START
事件,则使用表达式 PY_RETURN | PY_START
。
- sys.monitoring.events.NO_EVENTS¶
代表
0
的别名以便用户可以这样执行显式比较:if get_events(DEBUGGER_ID) == NO_EVENTS: ...
设置此事件将撤销所有事件的激活。
本地事件¶
本地事件与程序的正常执行相关联并且发生在明确定义的位置上。 所有本地事件都可以被禁用。 本地事件包括:
已弃用的事件¶
BRANCH
BRANCH
事件已在3.14中被弃用。 使用 BRANCH_LEFT
和 BRANCH_RIGHT
事件可以提供更好的性能,因为它们可以被单独禁用。
辅助事件¶
辅助事件可以像其他事件一样被监视,但是由另一个事件来控制:
C_RETURN
和 C_RAISE
事件是由 CALL
事件控制的。 C_RETURN
和 C_RAISE
事件只会在相应的 CALL
事件被监控时才能被看到。
其他事件¶
其他事件不一定与程序中的特定位置相关联并且不能被单独禁用。
可以被监视的其他事件包括:
STOP_ITERATION 事件¶
PEP 380 规定了当从生成器或协程返回值时可引发 StopIteration
异常。 不过,这是一种非常低效的返回值的方式,因此某些 Python 实现,比如 CPython 3.12+,只有在异常对其他代码可见时才会引发它。
为允许工具监视真正的异常而不会拖慢生成器和协程的运行,解释器提供了 STOP_ITERATION
事件。 STOP_ITERATION
可以被局部禁用,这与 RAISE
不同。
请注意,STOP_ITERATION
事件和 StopIteration
异常的 RAISE
事件是等价的,并且在生成事件时被视为可互换的。 出于性能原因,实现将倾向于 STOP_ITERATION
,但可能会使用 StopIteration
生成 RAISE
事件。
开启和关闭事件¶
要监视一个事件,它必须被开启且相应的回调必须被注册。 可以通过将事件设置为全局的和/或针对特定代码对象的来开启或关闭事件。 一个事件将只被触发一次,即使它在全局和局部都被开启。
全局设置事件¶
通过修改被监视的事件集可以对事件进行全局控制。
- sys.monitoring.set_events(tool_id: int, event_set: int, /) None ¶
激活在 event_set 中设置的所有事件。 如果 tool_id 未被使用则会引发
ValueError
。
在默认情况下没有被激活的事件。
针对特定代码对象的事件¶
事件也可以基于每个代码对象来控制。 下面定义的接受一个 types.CodeType
的函数应当准备好接受来自不是在 Python 中定义的类似对象 (参见 监控 C API)。
禁用事件¶
- 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_id 和 event 注册了另一个回调,它将被注销并返回。 在其他情况下
register_callback()
将返回None
。引发一个 审计事件,事件名称为
sys.monitoring.register_callback
,并传入参数func
。
函数可以通过调用 sys.monitoring.register_callback(tool_id, event, None)
来注销。
回调函数可在任何时候被注册或注销。
回调将只被调用一次,即使事件在全局和局部都被开启。 因此,如果一个事件可以被你的代码在全局和局部同时开启那么回调就需要被编写为同时处理两个触发器。
回调函数参数¶
- sys.monitoring.MISSING¶
一个传给回调函数表明该调用不附带任何参数的特殊值。
当一个激活的事件发生时,已注册的回调函数将被调用。 回调函数返回 DISABLE
以外的对象将没有任何效果。 不同的事件将提供具有不同参数的回调函数,如下所示:
-
func(code: CodeType, instruction_offset: int) -> object
-
func(code: CodeType, instruction_offset: int, retval: object) -> object
CALL
,C_RAISE
和C_RETURN
(特别地 arg0 可以为MISSING
):func(code: CodeType, instruction_offset: int, callable: object, arg0: object) -> object
code 代表调用所在的代码对象,而 callable 是将要被调用的对象(并因此触发事件)。 如果没有参数,arg0 将被设为
sys.monitoring.MISSING
。对于实例方法,callable 将是在类上找到的 arg0 设为该实例的函数对象(该实例即方法的
self
参数)。RAISE
,RERAISE
,EXCEPTION_HANDLED
,PY_UNWIND
,PY_THROW
和STOP_ITERATION
:func(code: CodeType, instruction_offset: int, exception: BaseException) -> object
LINE
:func(code: CodeType, line_number: int) -> object
BRANCH_LEFT
、BRANCH_RIGHT
和JUMP
:func(code: CodeType, instruction_offset: int, destination_offset: int) -> object
注意,destination_offset 是代码下一次执行的地方。
-
func(code: CodeType, instruction_offset: int) -> object