Runner
******

**ソースコード:** Lib/asyncio/runners.py

この節では、asyncio のコードを実行するための高レベルの asyncio のプリ
ミティブの概略を解説します。

これらは イベントループ の上に構築されており、一般的な広く普及している
シナリオでの非同期コードの使用を簡素化することを目的としています。

* 非同期プログラムの実行

* Runner コンテキストマネージャー

* キーボード割り込みの処理


非同期プログラムの実行
======================

asyncio.run(coro, *, debug=None, loop_factory=None)

   asyncio のイベントループで *coro* を実行し、その結果を返します。

   引数には任意の awaitable オブジェクトを指定できます。

   この関数は awaitable を実行し、asyncio のイベントループの管理、*非
   同期ジェネレーターの終了処理*、Executor の終了処理を行います。

   この関数は、同じスレッドで他の非同期イベントループが実行中のときは
   呼び出せません。

   *debug* が "True" の場合、イベントループはデバッグモードで実行され
   ます。"False" は明示的にデバッグモードを無効化します。 グローバルな
   デバッグモード 設定を尊重するために "None" が使用されます。

   *loop_factory* が "None" でなければ、それを使って新しいイベントルー
   プを作成します。 それ以外の場合は "asyncio.new_event_loop()" を使い
   ます。 終了時にループを閉じます。 この関数は asyncio プログラムのメ
   インのエントリーポイントとして使い、できれば一度だけ呼び出してくだ
   さい。 イベントループの設定には、ポリシーの代わりに *loop_factory*
   を使うことを推奨します。 "asyncio.EventLoop" を渡すと、ポリシーシス
   テムを使わずに asyncio を実行できます。

   Executor の終了を待つ制限時間は 5 分です。 その時間内に終了しなけれ
   ば、警告を出して Executor の終了処理を行います。

   以下はプログラム例です:

      async def main():
          await asyncio.sleep(1)
          print('hello')

      asyncio.run(main())

   Added in version 3.7.

   バージョン 3.9 で変更: "loop.shutdown_default_executor()" メソッド
   を使うように更新されました。

   バージョン 3.10 で変更: *debug* はグローバルなデバッグモード設定を
   尊重するためにデフォルトで "None" です。

   バージョン 3.12 で変更: *loop_factory* 引数が追加されました。

   バージョン 3.14 で変更: *coro* に任意の awaitable オブジェクトを指
   定できるようになりました。

   注釈:

     "asyncio" のポリシーシステムは非推奨で、Python 3.16 で削除されま
     す。 それ以降、イベントループを設定するには *loop_factory* を明示
     的に指定する必要があります。


Runner コンテキストマネージャー
===============================

class asyncio.Runner(*, debug=None, loop_factory=None)

   同じコンテキスト上での *複数* の非同期関数呼び出しをシンプルにする
   コンテキストマネージャ。

   複数のトップレベルの非同期関数を、同じ イベントループ と
   "contextvars.Context" で呼び出したい場合があります。

   *debug* が "True" の場合、イベントループはデバッグモードで実行され
   ます。"False" は明示的にデバッグモードを無効化します。 グローバルな
   デバッグモード 設定を尊重するために "None" が使用されます。

   *loop_factory* で、ループの作成方法を変更できます。 作成したループ
   を現在のループとして設定するのは *loop_factory* の役割です。
   *loop_factory* が "None" の場合は、デフォルトで
   "asyncio.new_event_loop()" を使い、"asyncio.set_event_loop()" で現
   在のイベントループに設定します。

   "asyncio.run()" の例は、Runner を使って次のように書き換えられます。

      async def main():
          await asyncio.sleep(1)
          print('hello')

      with asyncio.Runner() as runner:
          runner.run(main())

   Added in version 3.11.

   run(coro, *, context=None)

      内部のイベントループで *coro* を実行します。

      引数には任意の awaitable オブジェクトを指定できます。

      引数がコルーチンの場合、Task でラップします。

      省略可能なキーワード専用引数 *context* には、コードの実行に使う
      "contextvars.Context" を指定できます。 "context" が "None" の場
      合は、Runner のデフォルトのコンテキストを使います。

      awaitable の結果を返すか、例外を送出します。

      この関数は、同じスレッドで他の非同期イベントループが実行中のとき
      は呼び出せません。

      バージョン 3.14 で変更: *coro* に任意の awaitable オブジェクトを
      指定できるようになりました。

   close()

      Runner を閉じます。

      非同期ジェネレーターの終了処理を行い、デフォルトの Executor とイ
      ベントループを閉じ、内部の "contextvars.Context" を解放します。

   get_loop()

      Runner インスタンスに関連付けられたイベントループを返します。

   注釈:

     "Runner" は遅延初期化を採用しており、コンストラクターでは内部の低
     水準なデータ構造を初期化しません。内部の *loop* と *context* は、
     "with" 文の本体に入るとき、または "run()" か "get_loop()" を初め
     て呼び出すときに作成されます。


キーボード割り込みの処理
========================

Added in version 3.11.

"Ctrl"-"C" で "signal.SIGINT" が発生すると、デフォルトではメインスレッ
ドで "KeyboardInterrupt" が送出されます。 しかし "asyncio" では、内部
の処理が中断されてプログラムが終了できなくなる可能性があるため、この動
作は適していません。

この問題を軽減するため、"asyncio" は "signal.SIGINT" を次のように処理
します。

1. "asyncio.Runner.run()" はユーザーコードの実行前に独自の
   "signal.SIGINT" ハンドラーを設定し、関数を終了するときに取り除きま
   す。

2. "Runner" は、渡されたコルーチンを実行するメインタスクを作成します。

3. "Ctrl"-"C" によって "signal.SIGINT" が発生すると、独自のシグナルハ
   ンドラーが "asyncio.Task.cancel()" を呼び出してメインタスクをキャン
   セルします。 これにより、メインタスク内で "asyncio.CancelledError"
   が送出されます。 Python のスタックが巻き戻されるため、"try/except"
   や "try/finally" ブロックでリソースを解放できます。 メインタスクの
   キャンセル後、"asyncio.Runner.run()" は "KeyboardInterrupt" を送出
   します。

4. ユーザーが書いたループが処理を譲らずに繰り返し続け、
   "asyncio.Task.cancel()" で中断できない場合があります。 その場合、2
   回目の "Ctrl"-"C" はメインタスクをキャンセルせず、即座に
   "KeyboardInterrupt" を送出します。
