コルーチンと Task
*****************

この節では、コルーチンと Task を利用する高レベルの asyncio の API の概
略を解説します。

* コルーチン

* Awaitable

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

* Task の作成

* スリープ

* 並行な Task 実行

* キャンセルからの保護

* タイムアウト

* 要素の待機

* 外部スレッドからのスケジュール

* イントロスペクション

* Task オブジェクト

* Generator-based Coroutines


コルーチン
==========

async/await 構文で宣言されたコルーチンは、 asyncio を使ったアプリケー
ションを書く手法として好ましいです。 例えば、次のコードスニペット
(Python 3.7 以降が必要) は "hello" を出力し、そこから 1 秒待って
"world" を出力します:

   >>> import asyncio

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

   >>> asyncio.run(main())
   hello
   world

単にコルーチンを呼び出しただけでは、コルーチンの実行スケジュールは予約
されていないことに注意してください:

   >>> main()
   <coroutine object main at 0x1053bb7c8>

実際にコルーチンを走らせるために、 asyncio は3つの機構を提供しています
:

* 最上位のエントリーポイントである "main()" 関数を実行する
  "asyncio.run()" 関数 (上の例を参照してください。)

* コルーチンを await すること。次のコード片は 1 秒間待機した後に
  "hello" と出力し、 *更に* 2 秒間待機してから "world" と出力します:

     import asyncio
     import time

     async def say_after(delay, what):
         await asyncio.sleep(delay)
         print(what)

     async def main():
         print(f"started at {time.strftime('%X')}")

         await say_after(1, 'hello')
         await say_after(2, 'world')

         print(f"finished at {time.strftime('%X')}")

     asyncio.run(main())

  予想される出力:

     started at 17:13:52
     hello
     world
     finished at 17:13:55

* asyncio の "Tasks" としてコルーチンを並行して走らせる
  "asyncio.create_task()" 関数。

  上のコード例を編集して、ふたつの "say_after" コルーチンを *並行して*
  走らせてみましょう:

     async def main():
         task1 = asyncio.create_task(
             say_after(1, 'hello'))

         task2 = asyncio.create_task(
             say_after(2, 'world'))

         print(f"started at {time.strftime('%X')}")

         # Wait until both tasks are completed (should take
         # around 2 seconds.)
         await task1
         await task2

         print(f"finished at {time.strftime('%X')}")

  予想される出力が、スニペットの実行が前回よりも 1 秒早いことを示して
  いることに注意してください:

     started at 17:14:32
     hello
     world
     finished at 17:14:34


Awaitable
=========

あるオブジェクトを "await" 式の中で使うことができる場合、そのオブジェ
クトを **awaitable** オブジェクトと言います。多くの asyncio API は
awaitable を受け取るように設計されています。

*awaitable* オブジェクトには主に3つの種類があります: **コルーチン**,
**Task**, そして **Future** です

-[ コルーチン ]-

Python のコルーチンは *awaitable* であり、そのため他のコルーチンを待機
させられます:

   import asyncio

   async def nested():
       return 42

   async def main():
       # Nothing happens if we just call "nested()".
       # A coroutine object is created but not awaited,
       # so it *won't run at all*.
       nested()

       # Let's do it differently now and await it:
       print(await nested())  # will print "42".

   asyncio.run(main())

重要:

  このドキュメントにおいて「コルーチン」という用語は以下2つの密接に関
  連した概念に対して使用できます:

  * *コルーチン関数*: "async def" 関数;

  * *コルーチンオブジェクト*: *コルーチン関数* を呼び出すと返ってくる
    オブジェクト.

asyncio は、古くからある ジェネレータベース のコルーチンもサポートして
います。

-[ Task ]-

*Task* は、コルーチンを *並行に* スケジュールするのに使います。

"asyncio.create_task()" のような関数で、コルーチンが *Task* にラップさ
れているとき、自動的にコルーチンは即時実行されるようにスケジュールされ
ます:

   import asyncio

   async def nested():
       return 42

   async def main():
       # Schedule nested() to run soon concurrently
       # with "main()".
       task = asyncio.create_task(nested())

       # "task" can now be used to cancel "nested()", or
       # can simply be awaited to wait until it is complete:
       await task

   asyncio.run(main())

-[ Future ]-

"Future" は、非同期処理の **最終結果** を表現する特別な **低レベルの**
awaitable オブジェクトです。

Future オブジェクトが他の awaitable を *待機させている* と言うときは、
ある場所で Future が解決されるまでコルーチンが待機するということです。

asyncio での Future オブジェクトは、async/await と共に使用できるように
するため、コールバック形式のコードを使用できるように設計すべきです。

通常、アプリケーション水準のコードで Future オブジェクトを作る **必要
はありません** 。

Future オブジェクトはライブラリや asyncio の API で表に出ることもあり
、他の awaitable を待機させられます:

   async def main():
       await function_that_returns_a_future_object()

       # this is also valid:
       await asyncio.gather(
           function_that_returns_a_future_object(),
           some_python_coroutine()
       )

Future オブジェクトを返す低レベル関数の良い例は
"loop.run_in_executor()" です。


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

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

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

   この関数は、非同期イベントループの管理と *非同期ジェネレータの終了
   処理* を行いながら、渡されたコルーチンを実行します。

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

   *debug* が "True" の場合、イベントループはデバッグモードで実行され
   ます。

   この関数は常に新しいイベントループを作成し、終了したらそのイベント
   ループを閉じます。 この関数は非同期プログラムのメインのエントリーポ
   イントとして使われるべきで、理想的には 1 回だけ呼び出されるべきです
   。

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

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

      asyncio.run(main())

   バージョン 3.7 で追加: **重要:** この関数は Python 3.7 で *暫定
   API* として asyncio に追加されました。


Task の作成
===========

asyncio.create_task(coro)

   *coro* coroutine を "Task" でラップし、その実行をスケジュールします
   。 Task オブジェクトを返します。

   その Task オブジェクトは "get_running_loop()" から返されたループの
   中で実行されます。現在のスレッドに実行中のループが無い場合は、
   "RuntimeError" が送出されます。

   この関数は **Python 3.7 で追加** されました。 Python 3.7 より前では
   、代わりに低レベルの "asyncio.ensure_future()" 関数が使えます:

      async def coro():
          ...

      # In Python 3.7+
      task = asyncio.create_task(coro())
      ...

      # This works in all Python versions but is less readable
      task = asyncio.ensure_future(coro())
      ...

   バージョン 3.7 で追加.


スリープ
========

coroutine asyncio.sleep(delay, result=None, *, loop=None)

   *delay* 秒だけ停止します。

   *result* が提供されている場合は、コルーチン完了時にそれが呼び出し元
   に返されます。

   "sleep()" は常に現在の Task を一時中断し、他の Task が実行されるの
   を許可します。

   *loop* 引数は非推奨で、 Python 3.10 で削除される予定です。

   現在の時刻を5秒間、毎秒表示するコルーチンの例:

      import asyncio
      import datetime

      async def display_date():
          loop = asyncio.get_running_loop()
          end_time = loop.time() + 5.0
          while True:
              print(datetime.datetime.now())
              if (loop.time() + 1.0) >= end_time:
                  break
              await asyncio.sleep(1)

      asyncio.run(display_date())


並行な Task 実行
================

awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

   *aws* シーケンスにある awaitable オプジェクト を *並行* 実行します
   。

   *aws* にある awaitable がコルーチンである場合、自動的に Task として
   スケジュールされます。

   全ての awaitable が正常終了した場合、その結果は返り値を集めたリスト
   になります。 返り値の順序は、 *aws* での awaitable の順序に相当しま
   す。

   *return_exceptions* が "False" である場合(デフォルト)、"gather()"
   で await しているタスクに対して、最初の例外が直接伝えられます。
   *aws* に並んでいる他の awaitable は、**キャンセルされずに** 引き続
   いて実行されます。

   *return_exceptions* が "True" だった場合、例外は成功した結果と同じ
   ように取り扱われ、結果リストに集められます。

   "gather()" が *キャンセル* された場合、起動された全ての (未完了の)
   awaitable も *キャンセル* されます。

   *aws* シーケンスにある Task あるいは Future が *キャンセル* された
   場合、 "CancelledError" を送出したかのうように扱われます。つまり、
   この場合 "gather()" 呼び出しはキャンセル *されません*。 これは、起
   動された 1 つの Task あるいは Future のキャンセルが、他の Task ある
   いは Future のキャンセルを引き起こすのを避けるためです。

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

      import asyncio

      async def factorial(name, number):
          f = 1
          for i in range(2, number + 1):
              print(f"Task {name}: Compute factorial({i})...")
              await asyncio.sleep(1)
              f *= i
          print(f"Task {name}: factorial({number}) = {f}")

      async def main():
          # Schedule three calls *concurrently*:
          await asyncio.gather(
              factorial("A", 2),
              factorial("B", 3),
              factorial("C", 4),
          )

      asyncio.run(main())

      # Expected output:
      #
      #     Task A: Compute factorial(2)...
      #     Task B: Compute factorial(2)...
      #     Task C: Compute factorial(2)...
      #     Task A: factorial(2) = 2
      #     Task B: Compute factorial(3)...
      #     Task C: Compute factorial(3)...
      #     Task B: factorial(3) = 6
      #     Task C: Compute factorial(4)...
      #     Task C: factorial(4) = 24

   バージョン 3.7 で変更: *gather* 自身がキャンセルされた場合は、
   *return_exceptions* の値に関わらずキャンセルが伝搬されます。


キャンセルからの保護
====================

awaitable asyncio.shield(aw, *, loop=None)

   "キャンセル" から awaitable オプジェクト を保護します。

   *aw* がコルーチンだった場合、自動的に Task としてスケジュールされま
   す。

   文:

      res = await shield(something())

   は、以下と同じです

      res = await something()

   それを含むコルーチンがキャンセルされた場合を *除き*、"something()"
   内で動作している Task はキャンセルされません。 "something()" 側から
   見るとキャンセルは発生しません。 呼び出し元がキャンセルされた場合で
   も、 "await" 式は "CancelledError" を送出します。

   注意: "something()" が他の理由 (例えば、原因が自分自身) でキャンセ
   ルされた場合は "shield()" でも保護できません。

   完全にキャンセルを無視したい場合 (推奨はしません) は、 "shield()"
   関数は次のように try/except 節と組み合わせることになるでしょう:

      try:
          res = await shield(something())
      except CancelledError:
          res = None


タイムアウト
============

coroutine asyncio.wait_for(aw, timeout, *, loop=None)

   *aw* awaitable が、完了するかタイムアウトになるのを待ちます。

   *aw* がコルーチンだった場合、自動的に Task としてスケジュールされま
   す。

   *timeout* には "None" もしくは待つ秒数の浮動小数点数か整数を指定で
   きます。 *timeout* が "None" の場合、 Future が完了するまで待ちます
   。

   タイムアウトが起きた場合は、 Task をキャンセルし
   "asyncio.TimeoutError" を送出します。

   Task の "キャンセル" を避けるためには、 "shield()" の中にラップして
   ください。

   この関数は、 Future が実際にキャンセルされるまで待つので、全体の待
   ち時間は *timeout* を超えることもあります。

   待機が中止された場合 *aw* も中止されます。

   *loop* 引数は非推奨で、 Python 3.10 で削除される予定です。

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

      async def eternity():
          # Sleep for one hour
          await asyncio.sleep(3600)
          print('yay!')

      async def main():
          # Wait for at most 1 second
          try:
              await asyncio.wait_for(eternity(), timeout=1.0)
          except asyncio.TimeoutError:
              print('timeout!')

      asyncio.run(main())

      # Expected output:
      #
      #     timeout!

   バージョン 3.7 で変更: *aw* がタイムアウトでキャンセルされたとき、
   "wait_for" は *aw* がキャンセルされるまで待ちます。 以前は、すぐに
   "asyncio.TimeoutError" を送出していました。


要素の待機
==========

coroutine asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

   *aws* 集合にある awaitable オブジェクト を *並行* 実行し、
   *return_when* で指定した条件が満たされるまで待ちます。

   *aws* にある awaitable のどれかがコルーチンの場合、自動的に Task と
   してスケジュールされます。 コルーチンオブジェクトを "wait()" に直接
   渡すのは 紛らわしい振る舞い を引き起こすため非推奨です。

   Task と Future の 2 つ "(done, pending)" を返します。

   使い方:

      done, pending = await asyncio.wait(aws)

   *loop* 引数は非推奨で、 Python 3.10 で削除される予定です。

   *timeout* (浮動小数点数または整数) が指定されていたら、処理を返すの
   を待つ最大秒数を制御するのに使われます。

   この関数は "asyncio.TimeoutError" を送出しないことに注意してくださ
   い。 タイムアウトが起きたときに完了していなかった Future や Task は
   、2 つ目の集合の要素として返されるだけです。

   *return_when* でこの関数がいつ結果を返すか指定します。指定できる値
   は以下の 定数のどれか一つです:

   +-------------------------------+------------------------------------------+
   | 定数                          | 説明                                     |
   |===============================|==========================================|
   | "FIRST_COMPLETED"             | いずれかの Future が終了したかキャンセル |
   |                               | されたときに返します。                   |
   +-------------------------------+------------------------------------------+
   | "FIRST_EXCEPTION"             | いずれかの Future が例外の送出で終了した |
   |                               | 場合に返します。 例外を送出 したフューチ |
   |                               | ャがない場合は、"ALL_COMPLETED" と等価に |
   |                               | なります。                               |
   +-------------------------------+------------------------------------------+
   | "ALL_COMPLETED"               | すべての Future が終了したかキャンセルさ |
   |                               | れたときに返します。                     |
   +-------------------------------+------------------------------------------+

   "wait_for()" と異なり、  "wait()" はタイムアウトが起きたときに
   Future をキャンセルしません。

   注釈:

     "wait()" は自動的にコルーチンを Task としてスケジュールし、その後
     、暗黙的に作成された Task オブジェクトを組になった集合 "(done,
     pending)" に入れて返します。 従って、次のコードは予想した通りには
     動作しません:

        async def foo():
            return 42

        coro = foo()
        done, pending = await asyncio.wait({coro})

        if coro in done:
            # This branch will never be run!

     上のスクリプト片は次のように修正できます:

        async def foo():
            return 42

        task = asyncio.create_task(foo())
        done, pending = await asyncio.wait({task})

        if task in done:
            # Everything will work as expected now.

     "wait()" にコルーチンオブジェクトを直接渡すのは非推奨です。

asyncio.as_completed(aws, *, loop=None, timeout=None)

   Run awaitable objects in the *aws* set concurrently.  Return an
   iterator of "Future" objects. Each Future object returned
   represents the earliest result from the set of the remaining
   awaitables.

   全フューチャが終了する前にタイムアウトが発生した場合
   "asyncio.TimeoutError" を送出します。

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

      for f in as_completed(aws):
          earliest_result = await f
          # ...


外部スレッドからのスケジュール
==============================

asyncio.run_coroutine_threadsafe(coro, loop)

   与えられたイベントループにコルーチンを送ります。 この処理は、スレッ
   ドセーフです。

   他の OS スレッドから結果を待つための "concurrent.futures.Future" を
   返します。

   この関数は、イベントループが動作しているスレッドとは異なる OS スレ
   ッドから呼び出すためのものです。 例えば次のように使います:

      # Create a coroutine
      coro = asyncio.sleep(1, result=3)

      # Submit the coroutine to a given loop
      future = asyncio.run_coroutine_threadsafe(coro, loop)

      # Wait for the result with an optional timeout argument
      assert future.result(timeout) == 3

   コルーチンから例外が送出された場合、返された Future に通知されます
   。 これはイベントループの Task をキャンセルするのにも使えます:

      try:
          result = future.result(timeout)
      except asyncio.TimeoutError:
          print('The coroutine took too long, cancelling the task...')
          future.cancel()
      except Exception as exc:
          print(f'The coroutine raised an exception: {exc!r}')
      else:
          print(f'The coroutine returned: {result!r}')

   このドキュメントの asyncio-multithreading 節を参照してください。

   他の asyncio 関数とは異なり、この関数は明示的に渡される *loop* 引数
   を必要とします。

   バージョン 3.5.1 で追加.


イントロスペクション
====================

asyncio.current_task(loop=None)

   現在実行中の "Task" インスタンスを返します。実行中の Task が無い場
   合は "None" を返します。

   *loop* が "None" の場合、 "get_running_loop()" が現在のループを取得
   するのに使われます。

   バージョン 3.7 で追加.

asyncio.all_tasks(loop=None)

   ループで実行された "Task" オブジェクトでまだ完了していないものの集
   合を返します。

   *loop* が "None" の場合、 "get_running_loop()" は現在のループを取得
   するのに使われます。

   バージョン 3.7 で追加.


Task オブジェクト
=================

class asyncio.Task(coro, *, loop=None)

   Python コルーチン を実行する "Future 類" オブジェクトです。 スレッ
   ドセーフではありません。

   Task はイベントループのコルーチンを実行するのに使われます。 Future
   でコルーチンが待機している場合、 Task は自身のコルーチンの実行を一
   時停止させ、 Future の完了を待ちます。 Future が *完了* したら、
   Task が内包しているコルーチンの実行を再開します。

   イベントループは協調スケジューリングを使用します。つまり、イベント
   ループは同時に 1 つの Task のみ実行します。 Task が Future の完了を
   待っているときは、イベントループは他の Task やコールバックを動作さ
   せるか、 IO 処理を実行します。

   Task を作成するには高レベルの "asyncio.create_task()" 関数、あるい
   は低レベルの "loop.create_task()" 関数や "ensure_future()" 関数を使
   用してください。 手作業での Task の実装は推奨されません。

   実行中のタスクをキャンセルするためには、"cancel()" メソッドを使用し
   ます。このメソッドを呼ぶと、タスクはそれを内包するコルーチンに対し
   て "CancelledError" 例外を送出します。キャンセルの際にコルーチンが
   Future オブジェクトを待っていた場合、その Future オブジェクトはキャ
   ンセルされます。

   "cancelled()" は、タスクがキャンセルされたかを調べるのに使用できま
   す。タスクを内包するコルーチンで "CancelledError" 例外が抑制されて
   おらず、かつタスクが実際にキャンセルされている場合に、このメソッド
   は "True" を変えます。

   "asyncio.Task" は、"Future.set_result()" と
   "Future.set_exception()" を除いて、"Future" の API をすべて継承して
   います。

   Task は "contextvars" モジュールをサポートします。Task が作られたと
   きに現在のコンテキストがコピーされ、のちに Task のコルーチンを実行
   する際に、コピーされたコンテキストが使用されます。

   バージョン 3.7 で変更: "contextvars" モジュールのサポートを追加。

   cancel()

      このタスクに、自身のキャンセルを要求します。

      This arranges for a "CancelledError" exception to be thrown into
      the wrapped coroutine on the next cycle of the event loop.

      The coroutine then has a chance to clean up or even deny the
      request by suppressing the exception with a "try" ... ...
      "except CancelledError" ... "finally" block. Therefore, unlike
      "Future.cancel()", "Task.cancel()" does not guarantee that the
      Task will be cancelled, although suppressing cancellation
      completely is not common and is actively discouraged.

      The following example illustrates how coroutines can intercept
      the cancellation request:

         async def cancel_me():
             print('cancel_me(): before sleep')

             try:
                 # Wait for 1 hour
                 await asyncio.sleep(3600)
             except asyncio.CancelledError:
                 print('cancel_me(): cancel sleep')
                 raise
             finally:
                 print('cancel_me(): after sleep')

         async def main():
             # Create a "cancel_me" Task
             task = asyncio.create_task(cancel_me())

             # Wait for 1 second
             await asyncio.sleep(1)

             task.cancel()
             try:
                 await task
             except asyncio.CancelledError:
                 print("main(): cancel_me is cancelled now")

         asyncio.run(main())

         # Expected output:
         #
         #     cancel_me(): before sleep
         #     cancel_me(): cancel sleep
         #     cancel_me(): after sleep
         #     main(): cancel_me is cancelled now

   cancelled()

      Return "True" if the Task is *cancelled*.

      The Task is *cancelled* when the cancellation was requested with
      "cancel()" and the wrapped coroutine propagated the
      "CancelledError" exception thrown into it.

   done()

      Return "True" if the Task is *done*.

      A Task is *done* when the wrapped coroutine either returned a
      value, raised an exception, or the Task was cancelled.

   result()

      Return the result of the Task.

      If the Task is *done*, the result of the wrapped coroutine is
      returned (or if the coroutine raised an exception, that
      exception is re-raised.)

      If the Task has been *cancelled*, this method raises a
      "CancelledError" exception.

      If the Task's result isn't yet available, this method raises a
      "InvalidStateError" exception.

   exception()

      Return the exception of the Task.

      If the wrapped coroutine raised an exception that exception is
      returned.  If the wrapped coroutine returned normally this
      method returns "None".

      If the Task has been *cancelled*, this method raises a
      "CancelledError" exception.

      If the Task isn't *done* yet, this method raises an
      "InvalidStateError" exception.

   add_done_callback(callback, *, context=None)

      Add a callback to be run when the Task is *done*.

      This method should only be used in low-level callback-based
      code.

      See the documentation of "Future.add_done_callback()" for more
      details.

   remove_done_callback(callback)

      コールバックリストから *callback* を削除します。

      This method should only be used in low-level callback-based
      code.

      See the documentation of "Future.remove_done_callback()" for
      more details.

   get_stack(*, limit=None)

      Return the list of stack frames for this Task.

      If the wrapped coroutine is not done, this returns the stack
      where it is suspended.  If the coroutine has completed
      successfully or was cancelled, this returns an empty list. If
      the coroutine was terminated by an exception, this returns the
      list of traceback frames.

      フレームは常に古いものから新しい物へ並んでいます。

      Only one stack frame is returned for a suspended coroutine.

      The optional *limit* argument sets the maximum number of frames
      to return; by default all available frames are returned. The
      ordering of the returned list differs depending on whether a
      stack or a traceback is returned: the newest frames of a stack
      are returned, but the oldest frames of a traceback are returned.
      (This matches the behavior of the traceback module.)

   print_stack(*, limit=None, file=None)

      Print the stack or traceback for this Task.

      This produces output similar to that of the traceback module for
      the frames retrieved by "get_stack()".

      The *limit* argument is passed to "get_stack()" directly.

      The *file* argument is an I/O stream to which the output is
      written; by default output is written to "sys.stderr".

   classmethod all_tasks(loop=None)

      イベントループのすべての Task の集合を返します。

      By default all tasks for the current event loop are returned. If
      *loop* is "None", the "get_event_loop()" function is used to get
      the current loop.

      This method is **deprecated** and will be removed in Python 3.9.
      Use the "asyncio.all_tasks()" function instead.

   classmethod current_task(loop=None)

      Return the currently running task or "None".

      If *loop* is "None", the "get_event_loop()" function is used to
      get the current loop.

      This method is **deprecated** and will be removed in Python 3.9.
      Use the "asyncio.current_task()" function instead.


Generator-based Coroutines
==========================

注釈:

  Support for generator-based coroutines is **deprecated** and is
  scheduled for removal in Python 3.10.

Generator-based coroutines predate async/await syntax.  They are
Python generators that use "yield from" expressions to await on
Futures and other coroutines.

Generator-based coroutines should be decorated with
"@asyncio.coroutine", although this is not enforced.

@asyncio.coroutine

   Decorator to mark generator-based coroutines.

   This decorator enables legacy generator-based coroutines to be
   compatible with async/await code:

      @asyncio.coroutine
      def old_style_coroutine():
          yield from asyncio.sleep(1)

      async def main():
          await old_style_coroutine()

   This decorator is **deprecated** and is scheduled for removal in
   Python 3.10.

   This decorator should not be used for "async def" coroutines.

asyncio.iscoroutine(obj)

   *obj* が コルーチンオブジェクト であれば "True" を返します。

   This method is different from "inspect.iscoroutine()" because it
   returns "True" for generator-based coroutines.

asyncio.iscoroutinefunction(func)

   Return "True" if *func* is a coroutine function.

   This method is different from "inspect.iscoroutinefunction()"
   because it returns "True" for generator-based coroutine functions
   decorated with "@coroutine".
