Runners
*******

**Source code:** Lib/asyncio/runners.py

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

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

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

* Runner context manager

* Handling Keyboard Interruption


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

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

   *coroutine* *coro* を実行し、結果を返します。

   This function runs the passed coroutine, taking care of managing
   the asyncio event loop, *finalizing asynchronous generators*, and
   closing the threadpool.

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

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

   This function always creates a new event loop and closes it at the
   end.  It should be used as a main entry point for asyncio programs,
   and should ideally only be called once.

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

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

      asyncio.run(main())

   バージョン 3.7 で追加.

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

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


Runner context manager
======================

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

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

   Sometimes several top-level async functions should be called in the
   same event loop and "contextvars.Context".

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

   *loop_factory* could be used for overriding the loop creation. It
   is the responsibility of the *loop_factory* to set the created loop
   as the current one. By default "asyncio.new_event_loop()" is used
   and set as current event loop with "asyncio.set_event_loop()" if
   *loop_factory* is "None".

   Basically, "asyncio.run()" example can be rewritten with the runner
   usage:

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

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

   バージョン 3.11 で追加.

   run(coro, *, context=None)

      Run a *coroutine* *coro* in the embedded loop.

      Return the coroutine's result or raise its exception.

      An optional keyword-only *context* argument allows specifying a
      custom "contextvars.Context" for the *coro* to run in. The
      runner's default context is used if "None".

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

   close()

      Close the runner.

      Finalize asynchronous generators, shutdown default executor,
      close the event loop and release embedded "contextvars.Context".

   get_loop()

      Return the event loop associated with the runner instance.

   注釈:

     "Runner" uses the lazy initialization strategy, its constructor
     doesn't initialize underlying low-level structures.Embedded
     *loop* and *context* are created at the "with" body entering or
     the first call of "run()" or "get_loop()".


Handling Keyboard Interruption
==============================

バージョン 3.11 で追加.

When "signal.SIGINT" is raised by "Ctrl-C", "KeyboardInterrupt"
exception is raised in the main thread by default. However this
doesn't work with "asyncio" because it can interrupt asyncio internals
and can hang the program from exiting.

To mitigate this issue, "asyncio" handles "signal.SIGINT" as follows:

1. "asyncio.Runner.run()" installs a custom "signal.SIGINT" handler
   before any user code is executed and removes it when exiting from
   the function.

2. The "Runner" creates the main task for the passed coroutine for its
   execution.

3. When "signal.SIGINT" is raised by "Ctrl-C", the custom signal
   handler cancels the main task by calling "asyncio.Task.cancel()"
   which raises "asyncio.CancelledError" inside the main task.  This
   causes the Python stack to unwind, "try/except" and "try/finally"
   blocks can be used for resource cleanup.  After the main task is
   cancelled, "asyncio.Runner.run()" raises "KeyboardInterrupt".

4. A user could write a tight loop which cannot be interrupted by
   "asyncio.Task.cancel()", in which case the second following
   "Ctrl-C" immediately raises the "KeyboardInterrupt" without
   cancelling the main task.
