"sys.monitoring" --- 執行事件監控
*********************************

在 3.12 版被加入.

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

備註:

  "sys.monitoring" 是 "sys" 模組內的一個命名空間，不是一個獨立的模組，
  所以不需要 "import sys.monitoring"，只需 "import sys" 然後使用
  "sys.monitoring"。

此命名空間提供對啟動和控制事件監控所需的函式和常數的存取。

當程式執行時，會發生一些能被監控工具關注的事件。"sys.monitoring" 命名
空間提供了在發生欲關注事件時接收回呼的方法。

監控 API 由三個元件組成：

* 工具識別器

* 事件

* 回呼 (callbacks)


工具識別器
==========

工具識別器是一個整數和關聯的名稱。工具識別器用於防止工具相互干擾並允許
多個工具同時運作。目前工具是完全獨立的，不能用來互相監控。將來此限制可
能會取消。

在註冊或啟動事件之前，工具應選擇一個識別器。識別器是 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* 就應該被呼叫。

備註:

  "free_tool_id()" 不會停用與 *tool_id* 相關的全域或區域事件，也不會取
  消註冊任何回呼函式。這個函式只是用來通知虛擬機器 (VM) 不再使用特定的
  *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

   採取（或不採取）一個條件分支。

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

   虛擬機器指令即將被執行。

sys.monitoring.events.JUMP

   在控制流程圖中進行無條件跳轉。

sys.monitoring.events.LINE

   即將執行的指令與前一條指令的列號不同。

sys.monitoring.events.PY_RESUME

   Python 函式的繼續執行（對於產生器和協程函式），除了 "throw()" 呼叫
   。

sys.monitoring.events.PY_RETURN

   從 Python 函式回傳（發生在即將回傳之前，被呼叫者的 frame 將位於堆疊
   上）。

sys.monitoring.events.PY_START

   Python 函式的開始（在呼叫後立即發生，被呼叫者的 frame 將位於堆疊上
   ）

sys.monitoring.events.PY_THROW

   Python 函式透過 "throw()" 呼叫來繼續。

sys.monitoring.events.PY_UNWIND

   在例外展開 (unwind) 期間從 Python 函式退出。

sys.monitoring.events.PY_YIELD

   來自 Python 函式的 yield（在即將 yield 之前發生，被呼叫者的 frame
   將位於堆疊上）。

sys.monitoring.events.RAISE

   例外被引發，除了那些會導致 "STOP_ITERATION" 的事件外。

sys.monitoring.events.RERAISE

   例外被重新引發 (re-raise)，例如在 "finally" 區塊的最後。

sys.monitoring.events.STOP_ITERATION

   一個人為的 "StopIteration" 被引發；請參閱 STOP_ITERATION 事件。

將來可能會新增更多事件。

這些事件是 "sys.monitoring.events" 命名空間的屬性。每個事件以 2 次方的
整數常數表示。要定義一組事件，只要將個別事件以 bitwise or 相接即可。例
如，若要指定 "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"

* "STOP_ITERATION"


附屬事件 (ancillary events)
---------------------------

附屬事件可以像其他事件一樣被監控，但由另一個事件所控制：

* "C_RAISE"

* "C_RETURN"

"C_RETURN" 和 "C_RAISE" 事件由 "CALL" 事件控制。只有當對應的 "CALL" 事
件受到監控時，才會看到 "C_RETURN" 和 "C_RAISE" 事件。


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

其他事件不一定與程式中的特定位置相關聯，也不能單獨停用。

其他可以監控的事件有：

* "PY_THROW"

* "PY_UNWIND"

* "RAISE"

* "EXCEPTION_HANDLED"


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 中未定義函式的類似物
件（請參閱 監控 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" 來停用特定程式碼位置的區域
事件。這不會改變被設定的事件，或相同事件的任何其他程式碼位置。

停用特定位置的事件對於高效能監控非常重要。舉例來說，如果除少數斷點外，
偵錯器可以停用所有監控，那麼在偵錯器下執行程式就不會有任何開銷
(overhead)。

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(tool_id, event, None)"
來取消註冊函式。

回呼函式可以隨時被註冊和取消註冊。

註冊或取消註冊回呼函式將產生 "sys.audit()" 事件。


回呼函式引數
------------

sys.monitoring.MISSING

   傳遞給回呼函式的特殊值，表示該呼叫沒有引數。

當有效事件發生時，已註冊的回呼函式會被呼叫。不同的事件會為回呼函式提供
不同的引數，如下所示：

* "PY_START" 和 "PY_RESUME"：

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

* "PY_RETURN" 和 "PY_YIELD"：

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

* "CALL"、"C_RAISE" 和 "C_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_THROW" 和
  "STOP_ITERATION"：

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

* "LINE"：

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

* "BRANCH" 和 "JUMP"：

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

  請注意，*destination_offset* 是程式碼接下來要執行的地方。對於未採用
  的分支，這將是該分支之後的指令的偏移量。

* "INSTRUCTION"：

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