Python プロセス内の複数のインタープリタ

ほとんどの場合は埋め込む Python インタプリタは1つだけですが、いくつかの場合に同一プロセス内、あるいは同一スレッド内で、複数の独立したインタプリタを作成する必要があります。 これを可能にするのがサブインタプリタです。

「メイン」インタープリタは、ランタイムの初期化時に最初に作成されるインタープリタです。 通常、プロセス内の Python インタープリタはこれだけです。 サブインタープリタとは異なり、メインインタープリタはシグナル処理などの、プロセス全体に関わる固有の役割を担います。 ランタイムの初期化中の実行も担い、通常はランタイムの終了処理中にもアクティブなインタープリタです。 PyInterpreterState_Main() は、その状態へのポインターを返します。

サブインタプリタを切り替えが PyThreadState_Swap() 関数でできます。 次の関数を使ってサブインタプリタの作成と削除が行えます:

type PyInterpreterConfig

サブインタープリタの設定に使うパラメーターの大部分を含む構造体です。 値は Py_NewInterpreterFromConfig() でのみ使用され、ランタイムによって変更されることはありません。

Added in version 3.12.

構造体フィールド:

int use_main_obmalloc

0 の場合、サブインタープリタは独自の「オブジェクト」アロケーターの状態を使います。 それ以外の場合は、メインインタープリタの状態を共有して使います。

0 の場合、check_multi_interp_extensions は 1 (ゼロ以外) でなければなりません。 1 の場合、gil は PyInterpreterConfig_OWN_GIL であってはなりません。

int allow_fork

0 の場合、このサブインタープリタが現在アクティブなスレッドでは、ランタイムはプロセスの fork を許可しません。 それ以外の場合、fork に制限はありません。

fork を許可しなくても、subprocess モジュールは動作します。

int allow_exec

0 の場合、このサブインタープリタが現在アクティブなスレッドでは、ランタイムは exec (os.execv() など) による現在のプロセスの置き換えを許可しません。 それ以外の場合、exec に制限はありません。

exec を許可しなくても、subprocess モジュールは動作します。

int allow_threads

0 の場合、このサブインタープリタの threading モジュールはスレッドを作成しません。 それ以外の場合は、スレッドの作成が許可されます。

int allow_daemon_threads

0 の場合、このサブインタープリタの threading モジュールはデーモンスレッドを作成しません。 それ以外の場合は、allow_threads がゼロでない限り、デーモンスレッドの作成が許可されます。

int check_multi_interp_extensions

0 の場合、このサブインタープリタが現在アクティブなスレッドでは、従来の単相初期化モジュールを含め、すべての拡張モジュールをインポートできます。 それ以外の場合は、多相初期化を使う拡張モジュールのみインポートできます (PEP 489 を参照)。 Py_mod_multiple_interpreters も参照してください。

use_main_obmalloc が 0 の場合は、1 (ゼロ以外) でなければなりません。

int gil

サブインタープリタの GIL の動作を指定します。 次のいずれかの値を取ります。

PyInterpreterConfig_DEFAULT_GIL

デフォルトの選択 (PyInterpreterConfig_SHARED_GIL) を使います。

PyInterpreterConfig_SHARED_GIL

メインインタープリタの GIL を共有して使います。

PyInterpreterConfig_OWN_GIL

サブインタープリタ独自の GIL を使います。

PyInterpreterConfig_OWN_GIL の場合、PyInterpreterConfig.use_main_obmalloc は 0 でなければなりません。

PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)

新しいサブインタプリタ (sub-interpreter) を生成します。サブインタプリタとは、(ほぼ完全に) 個別に分割された Python コードの実行環境です。特に、新しいサブインタプリタは、 import されるモジュール全てについて個別のバージョンを持ち、これには基盤となるモジュール builtins, __main__ および sys も含まれます。ロード済みのモジュールからなるテーブル (sys.modules) およびモジュール検索パス (sys.path) もサブインタプリタ毎に別個のものになります。新たなサブインタプリタ環境には sys.argv 変数がありません。また、サブインタプリタは新たな標準 I/O ストリーム sys.stdin, sys.stdout, sys.stderr を持ちます (とはいえ、これらのストリームは根底にある同じファイル記述子を参照しています)。

config は、インタープリタの初期化に使うオプションを指定します。

成功時は、新しいサブインタープリタで最初に作成した スレッド状態 が tstate_p に設定されます。 このスレッド状態は アタッチされています。 実際のスレッドは作成しないことに注意してください。 以下のスレッド状態についての説明を参照してください。 新しいインタープリタの作成に失敗すると、tstate_p は NULL に設定されます。 例外の状態は アタッチされたスレッド状態 に保存されますが、それが存在しない場合もあるため、例外は設定されません。

ほかの Python/C API 関数と同様に、呼び出す前には アタッチされたスレッド状態 が必要ですが、戻るときにはデタッチされている場合があります。 成功時には、返されたスレッド状態が アタッチされています。 独自の GIL を持つサブインタープリタを作成した場合は、呼び出し元のインタープリタの アタッチされたスレッド状態 がデタッチされます。 関数が戻るときには、新しいインタープリタの スレッド状態 が現在のスレッドに アタッチされ、元のインタープリタの アタッチされていたスレッド状態 はデタッチされたままです。

Added in version 3.12.

サブインタープリタは、一部の機能を制限して互いに隔離したときに、最も効果を発揮します。

PyInterpreterConfig config = {
    .use_main_obmalloc = 0,
    .allow_fork = 0,
    .allow_exec = 0,
    .allow_threads = 1,
    .allow_daemon_threads = 0,
    .check_multi_interp_extensions = 1,
    .gil = PyInterpreterConfig_OWN_GIL,
};
PyThreadState *tstate = NULL;
PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config);
if (PyStatus_Exception(status)) {
    Py_ExitStatusException(status);
}

設定は短時間だけ使われ、変更されることはありません。 初期化中に、設定の値が PyInterpreterState の各種の値に変換されます。 設定の読み取り専用のコピーが、PyInterpreterState 内部に保存される場合もあります。

拡張モジュールは、インタープリタ間 (サブインタープリタを含む) で次のように共有されます。

  • PyModule_FromDefAndSpec() などの多相初期化を使うモジュールでは、インタープリタごとに別のモジュールオブジェクトを作成して初期化します。 これらのモジュールオブジェクトが共有するのは、C レベルの静的変数とグローバル変数だけです。

  • PyModule_Create() などの従来の 単相初期化 を使うモジュールでは、ある拡張を最初にインポートするときは通常どおり初期化し、そのモジュールの辞書の浅いコピーを保存します。 別のインタープリタ (サブインタープリタを含む) が同じ拡張をインポートすると、新しいモジュールを初期化して、保存したコピーの内容を設定します。 拡張の init 関数は呼び出しません。 その結果、モジュールの辞書内のオブジェクトがインタープリタ間で共有され、望ましくない動作を引き起こす可能性があります (以下の Bugs and caveats を参照)。

    Py_FinalizeEx() と Py_Initialize() を呼び出してインタープリタを完全に再初期化した後で、拡張をインポートする場合とは異なります。 その場合は、拡張の initmodule 関数が再び呼び出され ます。 多相初期化の場合と同様に、これらのモジュール間で共有されるのは、C レベルの静的変数とグローバル変数だけです。

PyThreadState *Py_NewInterpreter(void)
次に属します: Stable ABI.

新しいサブインタープリタを作成します。 基本的には、従来の動作を維持する設定で Py_NewInterpreterFromConfig() を呼び出すラッパーです。 作成されるサブインタープリタは隔離されておらず、メインインタープリタの GIL を共有し、fork/exec、デーモンスレッド、単相初期化モジュールを許可します。

void Py_EndInterpreter(PyThreadState *tstate)
次に属します: Stable ABI.

指定された スレッド状態 が表すインタープリタ (サブインタープリタを含む) を破棄します。 指定したスレッド状態は アタッチされていなければなりません。 呼び出しから戻るときには、アタッチされたスレッド状態 はありません。 このインタープリタに関連付けられたすべてのスレッド状態が破棄されます。

Py_FinalizeEx() は、その時点で明示的に破棄されていないサブインタープリタをすべて破棄します。

インタープリタごとの GIL

Added in version 3.12.

Py_NewInterpreterFromConfig() を使うと、独自の GIL を持つなど、ほかのインタープリタから完全に隔離されたサブインタープリタを作成できます。 この隔離の最大の利点は、ほかのインタープリタにブロックされたり、ほかのインタープリタをブロックしたりせずに Python コードを実行できることです。 そのため、1 つの Python プロセスで Python コードを実行するときに、複数の CPU コアを実際に活用できます。 また、隔離によって、単にスレッドを使うのとは異なる並行処理の方法も促されます (PEP 554 と PEP 684 を参照)。

隔離されたインタープリタを使うには、隔離を維持するよう注意しなければなりません。 特に、スレッドセーフである保証なしにオブジェクトや可変の状態を共有してはいけません。 参照カウントがあるため、None や (1, 5) のように、それ以外の点では不変なオブジェクトであっても、通常は共有できません。 単純ですが効率の劣る回避策として、ある状態やオブジェクトを使うすべての操作をグローバルなロックで保護する方法があります。 また、整数や文字列のように実質的に不変なオブジェクトは、不死 にすることで、参照カウントがあっても安全にできます。 実際に、組み込みのシングルトン、小さい整数、その他多くの組み込みオブジェクトにこの方法が使われています。

隔離を維持すれば、フリースレッド化に伴う複雑さなしに、複数のコアを使った計算を行えます。 隔離を維持できなければ、競合状態やデバッグの難しいクラッシュなど、フリースレッド化のあらゆる問題にさらされます。

Aside from that, one of the main challenges of using multiple isolated interpreters is how to communicate between them safely (not break isolation) and efficiently. The runtime and stdlib do not provide any standard approach to this yet. A future stdlib module would help mitigate the effort of preserving isolation and expose effective tools for communicating (and sharing) data between interpreters.

バグと注意事項

サブインタープリタとメインインタープリタは同じプロセスに属するため、相互の隔離は完全ではありません。 例えば、os.close() のような低水準のファイル操作を使うと、誤って、あるいは悪意を持って、ほかのインタープリタが開いたファイルに影響を与えられます。 また、拡張はインタープリタ間で共有されるため、正しく動作しないものもあります。 特に、単相初期化や静的なグローバル変数を使う場合に起こりやすくなります。 あるサブインタープリタで作成したオブジェクトを、別のインタープリタの名前空間に挿入することもできますが、できるだけ避けるべきです。

サブインタープリタ間で、ユーザー定義の関数、メソッド、インスタンス、クラスを共有しないよう、特に注意するべきです。 それらが実行するインポート操作によって、誤ったインタープリタのロード済みモジュールの辞書に影響する可能性があるためです。 これらのオブジェクトにたどり着けるオブジェクトの共有を避けることも、同様に重要です。

サブインタプリタを PyGILState_* API と組み合わせるのが難しいことにも注意してください。これらのAPIはPythonのスレッド状態とOSレベルスレッドが1対1で対応していることを前提にしていて、サブインタプリタが存在するとその前提が崩れるからです。対応する PyGILState_Ensure() と PyGILState_Release() の呼び出しのペアの間では、サブインタプリタの切り替えを行わないことを強く推奨します。さらに、(ctypes のような)これらのAPIを使ってPythonの外で作られたスレッドから Pythonコードを実行している拡張モジュールはサブインタプリタを使うと壊れる可能性があります。

高水準 API

type PyInterpreterState
次に属します: Stable ABI (不透明な構造体として).

このデータ構造体は、協調動作する多数のスレッド間で共有されている状態を表現します。同じインタプリタに属するスレッドはモジュール管理情報やその他いくつかの内部的な情報を共有しています。この構造体には公開 (public) のメンバはありません。

異なるインタプリタに属するスレッド間では、利用可能なメモリ、開かれているファイルデスクリプタなどといったプロセス状態を除いて、初期状態では何も共有されていません。GILもまた、スレッドがどのインタプリタに属しているかに関わらずすべてのスレッドで共有されています。

バージョン 3.12 で変更: PEP 684 により、インタープリタごとの GIL を持てるようになりました。 Py_NewInterpreterFromConfig() を参照してください。

PyInterpreterState *PyInterpreterState_Get(void)
次に属します: Stable ABI (バージョン 3.9 より).

現在のインタープリタを取得します。

アタッチされたスレッド状態 がなければ、致命的エラーを発生させます。 NULL を返すことはありません。

Added in version 3.9.

int64_t PyInterpreterState_GetID(PyInterpreterState *interp)
次に属します: Stable ABI (バージョン 3.7 より).

インタプリタの一意な ID を返します。 処理中に何かエラーが起きたら、 -1 が返され、エラーがセットされます。

呼び出し元には スレッド状態がアタッチされていなければなりません。

Added in version 3.7.

PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)
戻り値: 借用参照。 次に属します: Stable ABI (バージョン 3.8 より).

インタプリタ固有のデータを保持している辞書を返します。 この関数が NULL を返した場合は、ここまでで例外は送出されておらず、呼び出し側はインタプリタ固有の辞書は利用できないと考えなければなりません。

これは PyModule_GetState() の代わりにはなりません。 拡張がインタープリタ固有の状態情報を保存するには、そちらを使うべきです。

返される辞書は、インタープリタからの借用参照であり、インタープリタの終了まで有効です。

Added in version 3.8.

typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)

フレーム評価関数の型。

throwflag 引数は、ジェネレーターの throw() メソッドが使います。 ゼロ以外の場合は、現在の例外を処理します。

バージョン 3.9 で変更: tstate 引数を受け取るようになりました。

バージョン 3.11 で変更: frame 引数が PyFrameObject* から _PyInterpreterFrame* に変わりました。

_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)

フレーム評価関数を取得します。

PEP 523 「Adding a frame evaluation API to CPython」を参照してください。

Added in version 3.9.

void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)

フレーム評価関数を設定します。

PEP 523 「Adding a frame evaluation API to CPython」を参照してください。

Added in version 3.9.

void _PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp, int allow_specialization)

カスタムのフレーム評価器を使うときの特殊化を、有効または無効にします。

allow_specialization がゼロ以外の場合、カスタムのフレーム評価関数が設定されていても、適応的な特殊化によるバイトコードの特殊化を継続します。 allow_specialization がゼロの場合、カスタムのフレーム評価関数を設定すると特殊化が無効になります。 フレーム評価 API を使用している間、標準のインタープリタループは特殊化を解除し続けます。 この機能を活用するには、フレーム評価関数が特殊化されたオペコードを処理する必要があります。

Added in version 3.15.

int _PyInterpreterState_IsSpecializationEnabled(PyInterpreterState *interp)

インタープリタで適応的な特殊化が有効なら、ゼロ以外を返します。 カスタムのフレーム評価関数が設定されていない場合や、設定されていても allow_specialization が有効な場合には、特殊化が有効です。

Added in version 3.15.

低水準 API

次の全ての関数は Py_Initialize() の後に呼び出さなければなりません。

バージョン 3.7 で変更: Py_Initialize() が GIL を初期化し、スレッド状態をアタッチ するようになりました。

PyInterpreterState *PyInterpreterState_New()
次に属します: Stable ABI.

新しいインタープリタ状態オブジェクトを作成します。 アタッチされたスレッド状態 は必要ありませんが、この関数の呼び出しを直列化する必要があれば、存在しても構いません。

引数無しで 監査イベント cpython.PyInterpreterState_New を送出します。

void PyInterpreterState_Clear(PyInterpreterState *interp)
次に属します: Stable ABI.

インタープリタ状態オブジェクトのすべての情報をリセットします。 そのインタープリタの スレッド状態がアタッチされていなければなりません。

引数無しで 監査イベント cpython.PyInterpreterState_Clear を送出します。

void PyInterpreterState_Delete(PyInterpreterState *interp)
次に属します: Stable ABI.

インタープリタ状態オブジェクトを破棄します。 対象のインタープリタの スレッド状態がアタッチされていてはなりません。 インタープリタ状態は、事前に PyInterpreterState_Clear() を呼び出してリセットしておく必要があります。

高度なデバッガーのサポート

以下の関数は高度なデバッグツールでの使用のためだけのものです。

PyInterpreterState *PyInterpreterState_Head()

インタプリタ状態オブジェクトからなるリストのうち、先頭にあるものを返します。

PyInterpreterState *PyInterpreterState_Main()

メインインタプリタの状態オブジェクトを返します。

PyInterpreterState *PyInterpreterState_Next(PyInterpreterState *interp)

インタプリタ状態オブジェクトからなるリストのうち、interp の次にあるものを返します。

PyThreadState *PyInterpreterState_ThreadHead(PyInterpreterState *interp)

インタプリタ interp に関連付けられているスレッドからなるリストのうち、先頭にある PyThreadState オブジェクトを返します。

PyThreadState *PyThreadState_Next(PyThreadState *tstate)

tstate と同じ PyInterpreterState オブジェクトに属しているスレッド状態オブジェクトのうち、 tstate の次にあるものを返します。