"sys.monitoring" --- 执行事件监测
*********************************

Added in version 3.12.

======================================================================

备注:

  "sys.monitoring" 是 "sys" 模块内的一个命名空间，而非一个独立模块，因
  此不需要 "import sys.monitoring"，只需 "import sys" 然后使用
  "sys.monitoring" 即可。

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

当程序执行时，会发生执行监控工具可能感兴趣的事件。 "sys.monitoring" 命
名空间提供了在感兴趣的事件发生时接收回调的途径。

monitoring API 由三个部分组成：

* Tool identifiers

* Events

* 回调


工具标识符
==========

工具标识符是一个整数及其所关联的名称。工具标识符被用来防止工具之间的相
互干扰并允许同时操作多个工具。目前工具是完全独立的且不能被用于相互监控
。 这一限制在将来可能会被取消。

在注册或激活事件之前，工具应选择一个标识符。标识符是 0 到 5 的闭区间内
的整数。


注册和使用工具
--------------

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

   必须在 *tool_id* 可被使用之前调用。 *tool_id* 必须在 0 到 5 的闭区
   间内。如果 *tool_id* 已被使用则会引发 "ValueError".

sys.monitoring.clear_tool_id(tool_id: int, /) -> None

   注销与 *tool_id* 相关联的所有事件和回调函数。

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.RERAISE

   一个异常被重新引发，例如在 "finally" 代码块结束的时候。

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:
          ...

   设置此事件将撤销所有事件的激活。


本地事件
--------

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

* "PY_START"

* "PY_RESUME"

* "PY_RETURN"

* "PY_YIELD"

* "CALL"

* "LINE"

* "INSTRUCTION"

* "JUMP"

* "BRANCH_LEFT"

* "BRANCH_RIGHT"

* "STOP_ITERATION"


已弃用的事件
------------

* "BRANCH"

"BRANCH" 事件已在 3.14 中被弃用。使用 "BRANCH_LEFT" 和 "BRANCH_RIGHT"
事件可以提供更好的性能，因为它们可以被单独禁用。


辅助事件
--------

辅助事件可以像其他事件一样被监视，但是由另一个事件来控制：

* "C_RAISE"

* "C_RETURN"

"C_RETURN" 和 "C_RAISE" 事件是由 "CALL" 事件控制的。 "C_RETURN" 和
"C_RAISE" 事件只会在相应的 "CALL" 事件被监控时才能被看到。


其他事件
--------

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

可以被监视的其他事件包括：

* "PY_THROW"

* "PY_UNWIND"

* "RAISE"

* "EXCEPTION_HANDLED"


STOP_ITERATION 事件
-------------------

**PEP 380** 规定了当从生成器或协程返回值时会引发 "StopIteration" 异常
。不过，这是一种非常低效的返回值的方式，因此某些 Python 实现，比如
CPython 3.12+，只有在异常对其他代码可见时才会引发它。

为允许工具监视真正的异常而不会拖慢生成器和协程的运行，解释器提供了
"STOP_ITERATION" 事件。 "STOP_ITERATION" 可以被局部禁用，这与 "RAISE"
不同。

请注意，"STOP_ITERATION" 事件和 "StopIteration" 异常的 "RAISE" 事件是
等价的，并且在生成事件时被视为可互换的。出于性能原因，实现将倾向于
"STOP_ITERATION"，但可能会使用 "StopIteration" 生成 "RAISE" 事件。


开启和关闭事件
==============

要监视一个事件，它必须被开启且相应的回调必须被注册。可以通过将事件设置
为全局的和/或针对特定代码对象的来开启或关闭事件。 一个事件将只被触发一
次，即使它在全局和局部都被开启。


全局设置事件
------------

通过修改被监视的事件集可以对事件进行全局控制。

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 中定义的类似对象 (参见 监控 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

   激活在 *event_set* 中针对 *code* 设置的所有 局部事件。如果
   *tool_id* 未被使用则会引发 "ValueError"。


禁用事件
--------

sys.monitoring.DISABLE

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

可以通过从回调函数返回 "sys.monitoring.DISABLE" 来禁用特定代码位置上的
局部事件。这不会改变已设置的事件，也不会改变针对同一事件的其他代码位置
。

禁用特定位置的事件对高性能的监控非常重要。例如，如果调试器禁用了除几个
断点外的所有监控那么程序在调试器下运行时就不会产生额外的开销。

如果 "DISABLE" 是由针对某个 全局事件 的回调返回的，则将由解释器在一个
非专属的位置中引发 "ValueError" (也就是说，不会提供回溯)。

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" 以外的对象将没有任何效果。 不同的事件将提供具有不同参数的回
调函数，如下所示：

* "PY_START" 和 "PY_RESUME":

     func(code: CodeType, instruction_offset: int) -> object

* "PY_RETURN" 和 "PY_YIELD":

     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* 是代码下一次执行的地方。

* "INSTRUCTION":

     func(code: CodeType, instruction_offset: int) -> object
