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.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 正在使用中,則返回工具的名稱,否則返回 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_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;參見 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:
    ...

設定此事件將停用所有事件。

區域性事件

區域性事件與程式的正常執行相關聯,併發生在明確定義的位置。所有區域性事件都可以停用。區域性事件包括

已棄用的事件

  • BRANCH

BRANCH 事件在 3.14 中已棄用。使用 BRANCH_LEFTBRANCH_RIGHT 事件將提供更好的效能,因為它們可以獨立停用。

輔助事件

輔助事件可以像其他事件一樣被監控,但由另一個事件控制

C_RETURNC_RAISE 事件由 CALL 事件控制。C_RETURNC_RAISE 事件只有在監控相應的 CALL 事件時才可見。

其他事件

其他事件不一定與程式中的特定位置繫結,並且無法單獨停用。

可以監控的其他事件包括

STOP_ITERATION 事件

PEP 380 指定從生成器或協程返回一個值時會引發 StopIteration 異常。然而,這是一種非常低效的返回值方式,因此一些 Python 實現,特別是 CPython 3.12+,除非異常對其他程式碼可見,否則不會引發異常。

為了允許工具監控真實的異常而不會減慢生成器和協程的速度,提供了 STOP_ITERATION 事件。STOP_ITERATION 可以區域性停用,不像 RAISE

請注意,STOP_ITERATION 事件和 StopIteration 異常的 RAISE 事件是等效的,並且在生成事件時被視為可互換。出於效能原因,實現將優先選擇 STOP_ITERATION,但可能會生成帶有 StopIterationRAISE 事件。

開啟和關閉事件

為了監控事件,必須開啟它並註冊相應的回撥。可以透過全域性和/或針對特定程式碼物件設定事件來開啟或關閉事件。即使事件全域性和區域性都開啟,它也只會觸發一次。

全域性設定事件

可以透過修改受監控的事件集來全域性控制事件。

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 來停用特定程式碼位置的區域性事件。這不會更改已設定的事件或相同事件的任何其他程式碼位置。

停用特定位置的事件對於高效能監控非常重要。例如,如果偵錯程式停用除少數斷點之外的所有監控,則程式可以在沒有開銷的情況下在偵錯程式下執行。

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,其引數為 func

可以透過呼叫 sys.monitoring.register_callback(tool_id, event, None) 來取消註冊函式。

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

無論事件是全域性還是區域性開啟,回撥都只調用一次。因此,如果您的程式碼可以同時為全域性和區域性事件開啟一個事件,那麼回撥需要編寫以處理任一觸發器。

回撥函式引數

sys.monitoring.MISSING

一個特殊值,傳遞給回撥函式以指示呼叫沒有引數。

當活動事件發生時,會呼叫已註冊的回撥函式。返回除 DISABLE 之外的物件的函式將不起作用。不同的事件將向回撥函式提供不同的引數,如下所示

  • PY_STARTPY_RESUME

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

    func(code: CodeType, instruction_offset: int, retval: object) -> object
    
  • CALLC_RAISEC_RETURNarg0 可以專門是 MISSING

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

    code 表示正在進行呼叫的程式碼物件,而 callable 是即將被呼叫(並因此觸發事件)的物件。如果沒有引數,arg0 將設定為 sys.monitoring.MISSING

    對於例項方法,callable 將是類上找到的函式物件,arg0 設定為例項(即方法的 self 引數)。

  • RAISERERAISEEXCEPTION_HANDLEDPY_UNWINDPY_THROWSTOP_ITERATION

    func(code: CodeType, instruction_offset: int, exception: BaseException) -> object
    
  • LINE:

    func(code: CodeType, line_number: int) -> object
    
  • BRANCH_LEFTBRANCH_RIGHTJUMP

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

    請注意,destination_offset 是程式碼接下來將執行的位置。

  • INSTRUCTION:

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