"concurrent.interpreters" --- 同じプロセス内の複数のインタープリタ
******************************************************************

Added in version 3.14.

**ソースコード:** Lib/concurrent/interpreters

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

"concurrent.interpreters" モジュールは、低水準の "_interpreters" モジ
ュールの上に、より高水準のインターフェースを構築します。

このモジュールの主な目的は、インタープリタ (「サブインタープリタ」とも
呼ばれます) の管理と、その中での実行のための基本的な API を提供するこ
とです。 実行時には主に、現在のスレッドでインタープリタを切り替え、そ
の実行コンテキストで関数を呼び出します。

並行処理に関しては、インタープリタ自体 (およびこのモジュール) が提供す
るのはほぼ分離だけであり、それだけでは役に立ちません。 実際の並行処理
は、別途 "スレッド" によって利用できます。 後述の説明 を参照してくださ
い。

参考:

  "InterpreterPoolExecutor"
     使い慣れたインターフェースで、スレッドとインタープリタを組み合わ
     せます。

  拡張モジュールを分離する
     複数のインタープリタをサポートするように拡張モジュールを更新する
     方法。

  **PEP 554**

  **PEP 734**

  **PEP 684**

利用可能な環境: not WASI.

このモジュールは WebAssembly では動作しないか、利用不可です。詳しくは
、WebAssembly プラットフォーム を見てください。


主な注意点
==========

詳しい説明に入る前に、複数のインタープリタの使用について留意すべき点を
挙げます:

* デフォルトで 分離される

* 暗黙にスレッドは作成されない

* PyPI のすべてのパッケージが、複数のインタープリタでの使用をサポート
  しているわけではない


はじめに
========

「インタープリタ」は、実質的に Python ランタイムの実行コンテキストです
。 プログラムの実行に必要な状態をすべて含んでいます。 これには、インポ
ートの状態や組み込みオブジェクトなどが含まれます。 (メインスレッドしか
ない場合でも、各スレッドには現在のインタープリタの状態に加え、現在の例
外やバイトコードの評価ループに関する実行時の状態があります)。

The concept and functionality of the interpreter have been a part of
Python since version 2.2, but the feature was only available through
the C-API and not well known, and the isolation was relatively
incomplete until version 3.12.


複数のインタープリタと分離
--------------------------

Python の実装は、同じプロセス内で複数のインタープリタを使うことをサポ
ートする場合があります。 CPython はこれをサポートしています。 各インタ
ープリタは、他のインタープリタから実質的に分離されています (プロセス全
体で共有されるものに、慎重に管理された少数の例外があります)。

この分離は主に、プログラムを構成する別々の論理的な部分を明確に切り離し
、それらのやり取りを慎重に制御したい場合に役立ちます。

注釈:

  同じプロセス内ではメモリアクセスに対する制限がほとんどないため、技術
  的には、同じプロセス内のインタープリタを互いに厳密に分離することはで
  きません。 Python ランタイムは可能な限り分離しますが、拡張モジュール
  は容易にその分離を破ることができます。 したがって、互いのデータにア
  クセスできてはならないような、セキュリティが重要な状況では、複数のイ
  ンタープリタを使わないでください。


インタープリタ内での実行
------------------------

別のインタープリタ内で実行するには、現在のスレッドでそのインタープリタ
に切り替えてから、関数を呼び出します。 ランタイムは、現在のインタープ
リタの状態を使って関数を実行します。 "concurrent.interpreters" モジュ
ールは、インタープリタを作成・管理する基本的な API と、この切り替えと
呼び出しの操作を提供します。

この操作で、他のスレッドが自動的に開始されることはありません。 ただし
、そのための 補助機能 があります。 インタープリタ内で組み込みの
"exec()" を呼び出すための専用の補助機能もあります。

インタープリタ内で "exec()" (または "eval()") を呼び出すと、そのインタ
ープリタの "__main__" モジュールを「グローバル」名前空間として実行しま
す。 どのモジュールにも関連付けられていない関数も同様です。 これは、コ
マンドラインから起動したスクリプトが "__main__" モジュール内で動作する
のと同じです。


並行処理と並列処理
------------------

前述のとおり、インタープリタ自体は並行処理を提供しません。 厳密には、
ランタイムが *現在のスレッドで* 使う、分離された実行コンテキストを表す
ものです。 その分離はプロセスに似ていますが、スレッドと同様に、同じプ
ロセス内にあることによる効率のよさも得られます。

とはいえ、インタープリタは特定の形態の並行処理を自然にサポートします。
分離には強力な副次効果があり、async やスレッドとは異なる並行処理の手法
が可能になります。 その並行処理モデルは CSP やアクターモデルに似ており
、比較的理解しやすいものです。

単一のスレッドでも、Stackless のようにインタープリタを行き来して、この
並行処理モデルを活用できます。 しかし、このモデルはインタープリタと複
数のスレッドを組み合わせたときに、より役立ちます。 主な手順は、新しい
スレッドを起動し、その中で別のインタープリタに切り替えて、必要な処理を
実行することです。

メインスレッドだけで実行している場合でも、Python の各実スレッドには、
それぞれの *現在の* 実行コンテキストがあります。 複数のスレッドで同じ
インタープリタを使うことも、別々のインタープリタを使うこともできます。

大まかには、スレッドとインタープリタの組み合わせを、明示的に選んだもの
だけを共有するスレッドと考えることができます。

大きな利点として、インタープリタは *GIL* を共有しないほど十分に分離さ
れています。 そのため、スレッドと複数のインタープリタを組み合わせると
、複数コアでの完全な並列実行が可能になります。 (Python 3.12 以降はこの
ようになっています)。


インタープリタ間の通信
----------------------

実際には、複数のインタープリタが役立つのは、それらの間で通信する手段が
ある場合だけです。 通常は何らかのメッセージパッシングを使いますが、慎
重に管理した方法でデータを共有する場合もあります。

このため、"concurrent.interpreters" モジュールは "queue.Queue" の実装
を提供しており、"create_queue()" で利用できます。


オブジェクトの「共有」
----------------------

インタープリタ間で実際に共有されるデータは、*GIL* が提供するスレッドセ
ーフ性を失います。 拡張モジュールでは、これに対処するための選択肢がい
くつかあります。 しかし、Python コードでは、スレッドセーフ性がないため
、ごく一部の例外を除いてオブジェクトを実際に共有することはできません。
代わりにコピーを作成しなければならないため、ミュータブルなオブジェクト
の同期は保たれません。

デフォルトでは、ほとんどのオブジェクトは別のインタープリタに渡す際に
"pickle" でコピーされます。 イミュータブルな組み込みオブジェクトのほぼ
すべては、直接共有されるか、効率よくコピーされます。 例えば、次のもの
があります:

* "None"

* "bool" ("True" と "False")

* "bytes"

* "str"

* "int"

* "float"

* "tuple" (同様にサポートされているオブジェクトを要素とするもの)

インタープリタ間で実際にミュータブルなデータを共有する Python の型も、
少数ですが存在します:

* "memoryview"

* "Queue"


リファレンス
============

このモジュールは以下の関数を定義しています:

concurrent.interpreters.list_all()

   既存の各インタープリタに対応する "Interpreter" オブジェクトの
   "list" を返します。

concurrent.interpreters.get_current()

   現在実行中のインタープリタの "Interpreter" オブジェクトを返します。

concurrent.interpreters.get_main()

   メインインタープリタの "Interpreter" オブジェクトを返します。 これ
   は、*REPL* またはコマンドラインで指定されたスクリプトを実行するため
   に、ランタイムが作成したインタープリタです。 通常は、これが唯一のイ
   ンタープリタです。

concurrent.interpreters.create()

   新しい (実行中でない) Python インタープリタを初期化し、その
   "Interpreter" オブジェクトを返します。

concurrent.interpreters.create_queue(maxsize=0)

   インタープリタ間で使う新しいキューを初期化し、その "Queue" オブジェ
   クトを返します。

   *maxsize* は、キューに入れられる要素数の上限を設定する整数です。 こ
   のサイズに達すると、キューの要素が取り出されるまで挿入はブロックさ
   れます。 *maxsize* が 0 以下の場合、キューのサイズに上限はありませ
   ん。


インタープリタオブジェクト
--------------------------

class concurrent.interpreters.Interpreter(id)

   現在のプロセス内の一つのインタープリタです。

   通常、"Interpreter" を直接呼び出すべきではありません。 代わりに
   "create()" または他のモジュール関数を使ってください。

   id

      (読み出し専用)

      基となるインタープリタの ID です。

   whence

      (読み出し専用)

      インタープリタがどこから作成されたかを表す文字列です。

   is_running()

      インタープリタが現在 "__main__" モジュール内でコードを実行してい
      る場合は "True"、それ以外の場合は "False" を返します。

   close()

      インタープリタの終了処理を行い、破棄します。

   prepare_main(ns=None, **kwargs)

      インタープリタの "__main__" モジュール内にオブジェクトを束縛しま
      す。

      一部のオブジェクトは実際に共有され、効率よくコピーされるものもあ
      りますが、ほとんどは "pickle" を介してコピーされます。 オブジェ
      クトの「共有」 を参照してください。

   exec(code, /)

      指定されたソースコードを、現在のスレッドでそのインタープリタ内で
      実行します。

   call(callable, /, *args, **kwargs)

      指定された関数を、現在のスレッドでそのインタープリタ内で実行した
      結果を返します。

   call_in_thread(callable, /, *args, **kwargs)

      指定された関数を、新しいスレッドでそのインタープリタ内で実行しま
      す。


例外
----

exception concurrent.interpreters.InterpreterError

   "Exception" のサブクラスで、インタープリタに関するエラーが発生した
   場合に送出されます。

exception concurrent.interpreters.InterpreterNotFoundError

   "InterpreterError" のサブクラスで、対象のインタープリタがすでに存在
   しない場合に送出されます。

exception concurrent.interpreters.ExecutionFailed

   "InterpreterError" のサブクラスで、実行中のコードが捕捉されない例外
   を送出した場合に送出されます。

   excinfo

      別のインタープリタで送出された例外の基本的なスナップショットです
      。

exception concurrent.interpreters.NotShareableError

   "TypeError" のサブクラスで、オブジェクトを別のインタープリタに送れ
   ない場合に送出されます。


インタープリタ間での通信
------------------------

class concurrent.interpreters.Queue(id)

   インタープリタ間で使う低水準のキューのラッパです。 "queue.Queue" の
   メソッドのうち、"task_done()"、"join()"、"shutdown()" を除くすべて
   を実装しています。 基となるキューを作成できるのは "create_queue()"
   だけです。

   一部のオブジェクトは実際に共有され、効率よくコピーされるものもあり
   ますが、ほとんどは "pickle" を介してコピーされます。 オブジェクトの
   「共有」 を参照してください。

   id

      (読み出し専用)

      キューの ID です。

   qsize()

      キュー内の要素数を返します。

   empty()

      キューが空ならば "True" を、そうでなければ "False" を返します。

   full()

      キューがいっぱいの場合は "True"、それ以外の場合は "False" を返し
      ます。

   put(obj, block=True, timeout=None)

      *obj* をキューに入れます。 オプション引数 *block* が真で、
      *timeout* が "None" (デフォルト) の場合、必要に応じて空きができ
      るまでブロックします。 *timeout* が正の数の場合、最大 *timeout*
      秒ブロックし、その間に空きができなければ "QueueFull" 例外を送出
      します。 それ以外の場合 (*block* が偽の場合)、直ちに利用できる空
      きがあればオブジェクトをキューに入れ、なければ "QueueFull" 例外
      を送出します (この場合、*timeout* は無視されます)。

   put_nowait(obj)

      "put(obj, block=False)" と等価です。

   get(block=True, timeout=None)

      キューからオブジェクトを取り出して返します。 オプション引数
      *block* が真で、*timeout* が "None" (デフォルト) の場合、必要に
      応じてオブジェクトが利用できるまでブロックします。 *timeout* が
      正の数の場合、最大 *timeout* 秒ブロックし、その間にオブジェクト
      が利用できなければ "QueueEmpty" 例外を送出します。 それ以外の場
      合 (*block* が偽の場合)、直ちに利用できるオブジェクトがあればそ
      れを返し、なければ "QueueEmpty" 例外を送出します (この場合、
      *timeout* は無視されます)。

   get_nowait()

      "get(block=False)" と等価です。

exception concurrent.interpreters.QueueEmpty

   "queue.Empty" のサブクラスで、キューが空の場合に "Queue.get()" と
   "Queue.get_nowait()" から送出されます。

exception concurrent.interpreters.QueueFull

   "queue.Full" のサブクラスで、キューがいっぱいの場合に "Queue.put()"
   と "Queue.put_nowait()" から送出されます。


基本的な使い方
==============

インタープリタを作成し、その中でコードを実行する例。

   from concurrent import interpreters

   interp = interpreters.create()

   # 現在の OS スレッドで実行する

   interp.exec('print("spam!")')

   interp.exec("""if True:
       print('spam!')
       """)

   from textwrap import dedent
   interp.exec(dedent("""
       print('spam!')
       """))

   def run(arg):
       return arg

   res = interp.call(run, 'spam!')
   print(res)

   def run():
       print('spam!')

   interp.call(run)

   # 新しい OS スレッドで実行する

   t = interp.call_in_thread(run)
   t.join()

キューを使ってインタープリタ間で通信する例。

   from concurrent import interpreters

   interp = interpreters.create()
   queue = interpreters.create_queue()

   def worker(queue):
       queue.put('spam!')

   t = interp.call_in_thread(worker, queue)
   print(queue.get())
   t.join()
