sys.monitoring --- 実行イベントモニタリング

Added in version 3.12.


注釈

sys.monitoring は sys モジュール内の名前空間で、独立したモジュールではありません。 import sys.monitoring は ModuleNotFoundError で失敗します。 代わりに import sys を実行し、sys.monitoring を使ってください。

この名前空間は、イベントモニタリングの有効化と制御に必要な関数と定数へのアクセスを提供します。

プログラムの実行中には、実行を監視するツールが関心を持つさまざまなイベントが発生します。 sys.monitoring 名前空間は、対象のイベントが発生したときにコールバックを受け取る手段を提供します。

モニタリング API は 3 つの要素から構成されます:

ツール識別子

ツール識別子は、整数とそれに関連付けられた名前からなります。 ツール識別子を使うことで、ツール同士の干渉を抑え、複数のツールが同時に動作できるようにします。 現在、ツールは完全に独立しており、互いを監視することはできません。 この制約は将来なくなる可能性があります。

イベントを登録または有効化する前に、ツールは識別子を選ぶ必要があります。 識別子は 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 に関連付けられたすべてのイベントとコールバック関数の登録を解除します。

Added in version 3.14.

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

ツールで tool_id が不要になったら呼び出してください。 tool_id を解放する前に clear_tool_id() を呼び出します。

バージョン 3.14 で変更: tool_id を解放する前に clear_tool_id() を呼び出すようになりました。 以前は、tool_id に関連付けられたグローバルイベントやローカルイベントを無効にせず、コールバック関数の登録も解除していませんでした。

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

tool_id が使用中ならツール名を返し、そうでなければ None を返します。 tool_id は 0 から 5 までの整数でなければなりません。

VM はイベントについてすべての 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

throw() の呼び出しによって Python 関数が再開される。

sys.monitoring.events.PY_UNWIND

例外によるスタックの巻き戻し中に Python 関数を抜ける。 関数内で直接送出され、そのまま外部へ伝播する例外も含む。

sys.monitoring.events.PY_YIELD

Python 関数からの yield (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_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 を監視している場合だけです。

その他のイベント

その他のイベントは、必ずしもプログラム内の特定の位置に結び付いておらず、位置ごとに個別に無効にすることはできません。

監視できるその他のイベントは次のとおりです:

バージョン 3.15 で変更: その他のイベントも、コードオブジェクト単位で有効・無効にできるようになりました。 コールバックから DISABLE を返すと、現在のツールについて、そのコードオブジェクト全体でイベントを無効にします。

STOP_ITERATION イベント

PEP 380 では、ジェネレーターやコルーチンから値を返すときに StopIteration 例外を送出すると定めています。 ただし、この方法で値を返すのは非常に非効率です。 そのため、一部の Python 実装、特に CPython 3.12 以降では、ほかのコードから見える場合を除いて例外を送出しません。

ジェネレーターやコルーチンを遅くせずに実際の例外を監視できるよう、STOP_ITERATION イベントを用意しています。 STOP_ITERATION はローカルに無効にできます。

STOP_ITERATION イベントと、StopIteration 例外に対する RAISE イベントは同等で、イベントの生成時には互いに置き換えられるものとして扱われます。 実装はパフォーマンス上の理由で STOP_ITERATION を優先しますが、StopIteration を伴う RAISE イベントを生成する場合もあります。

イベントの有効化と無効化

イベントを監視するには、そのイベントを有効にし、対応するコールバックを登録する必要があります。 イベントはグローバルに、特定のコードオブジェクトに対して、またはその両方で設定して、有効・無効にできます。 グローバルとローカルの両方で有効にしても、イベントは 1 回だけ発生します。

イベントのグローバルな設定

監視するイベントの集合を変更することで、イベントをグローバルに制御できます。

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 を送出します。

デフォルトでは、有効なイベントはありません。

コードオブジェクトごとのイベント

Events can also be controlled on a per code object basis. The functions defined below which accept a types.CodeType should be prepared to accept a look-alike object from functions which are not defined in Python (see モニタリング 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.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 を返します。

引数 func を指定して 監査イベント sys.monitoring.register_callback を送出します。

sys.monitoring.register_callback(tool_id, event, None) を呼び出すと、関数の登録を解除できます。

コールバック関数はいつでも登録・登録解除できます。

イベントがグローバルとローカルの両方で有効になっていても、コールバックは 1 回だけ呼び出されます。 そのため、コードがイベントをグローバルとローカルの両方で有効にする可能性がある場合は、どちらによる呼び出しにも対応できるようにコールバックを書く必要があります。

コールバック関数の引数

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