イベントループ
**************

**ソースコード:** Lib/asyncio/events.py, Lib/asyncio/base_events.py

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

-[ まえがき ]-

イベントループは全ての asyncio アプリケーションの中核をなす存在です。
イベントループは非同期タスクやコールバックを実行し、ネットワーク I/O
を処理し、サブプロセスを実行します。

アプリケーション開発者は通常 "asyncio.run()" のような高水準の ayncio
関数だけを利用し、ループオブジェクトを参照したり、ループオブジェクトの
メソッドを呼び出したりすることはほとんどありません。この節は、イベント
ループの振る舞いに対して細かい調整が必要な、低水準のコード、ライブラリ
、フレームワークの開発者向けです。

-[ イベントループの取得 ]-

以下の低水準関数はイベントループの取得、設定、生成するために使います:

asyncio.get_running_loop()

   現在の OS スレッドで実行中のイベントループを取得します。

   実行中のイベントループがなければ "RuntimeError" を送出します。

   この関数は、コルーチンまたはコールバックからのみ呼び出せます。

   Added in version 3.7.

asyncio.get_event_loop()

   現在のイベントループを取得します。

   コルーチンまたはコールバック ("call_soon()" などの API でスケジュー
   ルしたもの) から呼び出すと、常に実行中のイベントループを返します。

   実行中のイベントループがなければ、
   "get_event_loop_policy().get_event_loop()" の呼び出し結果を返します
   。

   この関数の振る舞いは (特にイベントループポリシーをカスタマイズした
   場合) 複雑なため、コルーチンやコールバックでは "get_event_loop()"
   よりも "get_running_loop()" を使うほうが好ましいと考えられます。

   前述のとおり、これらの低水準関数でイベントループを手動で作成・クロ
   ーズする代わりに、より高水準な "asyncio.run()" の使用を検討してくだ
   さい。

   バージョン 3.14 で変更: 現在のイベントループがなければ
   "RuntimeError" を送出するようになりました。

   注釈:

     "asyncio" のポリシーシステムは非推奨で、Python 3.16 で削除されま
     す。 それ以降、この関数は実行中のイベントループがあればそれを返し
     、なければ "set_event_loop()" で設定したループを返します。

asyncio.set_event_loop(loop)

   *loop* を OS スレッドの現在のイベントループに設定します。

asyncio.new_event_loop()

   新しいイベントループオブジェクトを生成して返します。

"get_event_loop()", "set_event_loop()", および "new_event_loop()" 関数
の振る舞いは、 カスタムイベントループポリシーを設定する ことにより変更
することができます。

-[ 内容 ]-

このページは以下の節から構成されます:

* イベントループのメソッド 節は、イベントループ API のリファレンスです
  。

* コールバックハンドル 節は "loop.call_soon()" や "loop.call_later()"
  などのスケジューリングメソッドが返す "Handle" や "TimerHandle" イン
  スタンスについて解説しています。

* サーバーオブジェクト 節は "loop.create_server()" のようなメソッドが
  返す型について解説しています。

* イベントループの実装 節は "SelectorEventLoop" と "ProactorEventLoop"
  の2つのクラスについて解説しています。

* 使用例 節ではイベントループ API の具体的な使い方を紹介しています。


イベントループのメソッド
========================

イベントループは以下の **低水準な** API を持っています:

* ループの開始と停止

* コールバックのスケジューリング

* 遅延コールバックのスケジューリング

* Future とタスクの作成

* ネットワーク接続の確立

* ネットワークサーバの生成

* ファイルの転送

* TLS へのアップグレード

* ファイル記述子の監視

* ソケットオブジェクトと直接やりとりする

* DNS

* パイプとやりとりする

* Unix シグナル

* スレッドまたはプロセスプールでコードを実行する

* エラー処理 API

* デバッグモードの有効化

* サブプロセスの実行


ループの開始と停止
------------------

loop.run_until_complete(future)

   *future* ("Future" のインスタンス) が完了するまで実行します。

   引数が コルーチンオブジェクト の場合、暗黙のうちに "asyncio.Task"
   として実行されるようにスケジュールされます。

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

loop.run_forever()

   "stop()" が呼び出されるまでイベントループを実行します。

   "run_forever()" メソッドが呼ばれるより前に "stop()" メソッドが呼ば
   れた場合、イベントループはタイムアウトをゼロにして一度だけ I/O セレ
   クタの問い合わせ処理を行い、 I/O イベントに対してスケジュールされた
   全てのコールバック (および既にスケジュール済みのコールバック) を実
   行したのち、終了します。

   "run_forever()" メソッドを実行中に "stop()" メソッドが呼び出された
   場合、イベントループは現在処理されているすべてのコールバックを実行
   してから終了します。 この場合、コールバックにより新たにスケジュール
   されるコールバックは実行されないことに注意してください; これら新た
   にスケジュールされたコールバックは、次に "run_forever()" または
   "run_until_complete()" が呼び出されたときに実行されます。

loop.stop()

   イベントループを停止します。

loop.is_running()

   イベントループが現在実行中の場合 "True" を返します。

loop.is_closed()

   イベントループが閉じられていた場合 "True" を返します。

loop.close()

   イベントループをクローズします。

   この関数が呼び出される時点で、イベントループが実行中であってはいけ
   ません。保留中のコールバックはすべて破棄されます。

   このメソッドは全てのキューをクリアし、エグゼキューターが実行完了す
   るのを待たずにシャットダウンします。

   このメソッドはべき等 (何回実行しても結果は同じ) であり取り消せませ
   ん。イベントループがクローズされた後、他のいかなるメソッドも呼び出
   すべきではありません。

async loop.shutdown_asyncgens()

   現在オープンになっているすべての *asynchronous generator* (非同期ジ
   ェネレータ) オブジェクトをスケジュールし、 "aclose()" メソッドを呼
   び出すことでそれらをクローズします。 このメソッドの呼び出し後に新し
   い非同期ジェネレータがイテレートされると、イベントループは警告を発
   します。このメソッドはスケジュールされたすべての非同期ジェネレータ
   の終了処理を確実に行うために使用すべきです。

   "asyncio.run()" を使った場合はこの関数を呼び出す必要はありません。

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

      try:
          loop.run_forever()
      finally:
          loop.run_until_complete(loop.shutdown_asyncgens())
          loop.close()

   Added in version 3.6.

async loop.shutdown_default_executor(timeout=None)

   デフォルトの Executor の終了をスケジュールし、"ThreadPoolExecutor"
   のすべてのスレッドの終了を待ちます。 このメソッドを呼び出した後で
   "loop.run_in_executor()" によりデフォルトの Executor を使うと、
   "RuntimeError" が送出されます。

   *timeout* は、Executor のスレッドの終了を待つ制限時間を秒単位の
   "float" で指定します。 デフォルトの "None" では、待ち時間に制限はあ
   りません。

   *timeout* に達すると "RuntimeWarning" を出し、スレッドの終了を待た
   ずにデフォルトの Executor の終了処理を行います。

   注釈:

     "asyncio.run()" はデフォルトの Executor の終了処理を自動的に行う
     ため、これを使う場合はこのメソッドを呼び出さないでください。

   Added in version 3.9.

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


コールバックのスケジューリング
------------------------------

loop.call_soon(callback, *args, context=None)

   イベントループの次のイテレーションで *callback* に指定したコールバ
   ック (*callback*) を *args* 引数で呼び出すようにスケジュールします
   。

   "asyncio.Handle" のインスタンスを返します。このインスタンスを使って
   スケジュールしたコールバックをキャンセルすることができます。

   コールバックは登録された順に呼び出されます。各コールバックは厳密に1
   回だけ呼び出されます。

   省略可能なキーワード専用引数 *context* には、*callback* の実行に使
   う "contextvars.Context" を指定します。 *context* を指定しなければ
   、現在のコンテキストを使います。

   "call_soon_threadsafe()" とは異なり、このメソッドはスレッドセーフで
   はありません。

loop.call_soon_threadsafe(callback, *args, context=None)

   "call_soon()" のスレッドセーフな版です。 "call_soon()" はスレッドセ
   ーフではないため、別のスレッドからコールバックをスケジュールする場
   合は *必ず* この関数を使ってください。

   この関数は、再入可能なコンテキストやシグナルハンドラーから安全に呼
   び出せます。 ただし、そのようなコンテキストで返されたハンドルを使う
   ことは、安全でも有用でもありません。

   すでにクローズされたイベントループに対してこのメソッドが呼び出され
   た場合 "RuntimeError" 例外を送出します。これはメインアプリケーショ
   ンが終了しているにもかかわらずセカンダリスレッドでメソッドが呼び出
   されるといった場合に起こりえます。

   このドキュメントの 並行処理とマルチスレッド処理 節を参照してくださ
   い。

   バージョン 3.7 で変更: キーワード引数 *context* が追加されました。
   詳細は **PEP 567** を参照してください。

注釈:

  ほとんどの "asyncio" モジュールのスケジューリング関数は、キーワード
  引数をコールバックに渡すことを許していません。キーワード引数を渡すた
  めには "functools.partial()" を使ってください:

     # print("Hello", flush=True) をスケジュールする
     loop.call_soon(
         functools.partial(print, "Hello", flush=True))

  asyncio は partial オブジェクトのデバッグメッセージやエラーメッセー
  ジをよりよく可視化することができるため、通常はラムダ式よりも partial
  オブジェクトを使う方が便利です。


遅延コールバックのスケジューリング
----------------------------------

イベントループは、コールバック関数を未来のある時点で呼び出されるように
スケジュールする仕組みを提供します。イベントループは時刻が戻らない単調
な時計 (monotonic clock) を使って時刻を追跡します。

loop.call_later(delay, callback, *args, context=None)

   *delay* 秒経過後にコールバック関数 *callback* を呼び出すようにスケ
   ジュールします。 *delay* には整数または浮動小数点数を指定します。

   "asyncio.TimerHandle" のインスタンスを返します。このインスタンスを
   使ってスケジュールしたコールバックをキャンセルすることができます。

   *callback* は厳密に一度だけ呼び出されます。2つのコールバックが完全
   に同じ時間にスケジュールされた場合、呼び出しの順序は未定義です。

   省略可能な位置引数 *args* は、コールバックの呼び出し時に渡されます
   。 *callback* に キーワード引数を渡す には、"functools.partial()"
   を使ってください。

   オプションのキーワード引数 *context* を使って、コールバック
   *callback* を実行する際のコンテキスト "contextvars.Context" を設定
   することができます。コンテキスト *context* が指定されない場合は現在
   のコンテキストが使われます。

   注釈:

     性能上の理由から、"loop.call_later()" でスケジュールしたコールバ
     ックは、時計の分解能の 1 単位分だけ早く実行される可能性があります
     ("time.get_clock_info('monotonic').resolution" を参照)。

   バージョン 3.7 で変更: キーワード引数 *context* が追加されました。
   詳細は **PEP 567** を参照してください。

   バージョン 3.8 で変更: Python 3.7 またはそれ以前のバージョンでは、
   デフォルトイベントループの実装を利用した場合に遅延時間 *delay* が1
   日を超えることができませんでした。この問題は Python 3.8 で修正され
   ました。

loop.call_at(when, callback, *args, context=None)

   絶対値の時刻 *when* (整数または浮動小数点数) にコールバックを呼び出
   すようにスケジュールします。 "loop.time()" と同じ参照時刻を使用しま
   す。

   このメソッドの振る舞いは "call_later()" と同じです。

   "asyncio.TimerHandle" のインスタンスを返します。このインスタンスを
   使ってスケジュールしたコールバックをキャンセルすることができます。

   注釈:

     性能上の理由から、"loop.call_at()" でスケジュールしたコールバック
     は、時計の分解能の 1 単位分だけ早く実行される可能性があります
     ("time.get_clock_info('monotonic').resolution" を参照)。

   バージョン 3.7 で変更: キーワード引数 *context* が追加されました。
   詳細は **PEP 567** を参照してください。

   バージョン 3.8 で変更: Python 3.7 またはそれ以前のバージョンでは、
   デフォルトイベントループの実装を利用した場合に現在の時刻と *when*
   との差が1日を超えることができませんでした。この問題は Python 3.8 で
   修正されました。

loop.time()

   現在の時刻を "float" 値で返します。時刻はイベントループが内部で参照
   している時刻が戻らない単調な時計 (monotonic clock) に従います。

注釈:

  バージョン 3.8 で変更: Python 3.7 またはそれ以前のバージョンでは、タ
  イムアウト (相対値 *delay* もしくは絶対値 *when*) は1日を超えること
  ができませんでした。この問題は Python 3.8 で修正されました。

参考: 関数 "asyncio.sleep()"。


Future とタスクの作成
---------------------

loop.create_future()

   イベントループに接続した "asyncio.Future" オブジェクトを生成します
   。

   asyncio でフューチャーオブジェクトを作成するために推奨される方法で
   す。このメソッドにより、サードパーティ製のイベントループがFutures
   クラスの(パフォーマンスや計測方法が優れた) 代替実装を提供することを
   可能にします。

   Added in version 3.5.2.

loop.create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)

   コルーチン  *coro* の実行をスケジュールします。 "Task" オブジェクト
   を返します。

   サードパーティのイベントループは相互運用のための自身の "Task" のサ
   ブクラスを使用できます。この場合、結果は "Task" のサブクラスになり
   ます。

   関数のシグネチャ全体は "Task" のコンストラクター (またはファクトリ
   ー) とほぼ同じで、この関数に渡したキーワード引数はすべて、その呼び
   出し先に渡されます。

   *name* 引数が指定され、値が "None" でない場合、 "Task.set_name()"
   メソッドにより *name* がタスクの名前として設定されます。

   省略可能なキーワード引数 *context* によって、*coro* を実行するため
   のカスタムの "contextvars.Context" を指定できます。*context* が省略
   された場合、現在のコンテキストのコピーが作成されます。

   省略可能なキーワード専用引数 *eager_start* は、"create_task()" の呼
   び出し中にタスクの実行を即座に開始するか、後で実行するようスケジュ
   ールするかを指定します。 *eager_start* を渡さなければ、
   "loop.set_task_factory()" で設定したモードが使われます。

   バージョン 3.8 で変更: *name* パラメータを追加しました。

   バージョン 3.11 で変更: *context* パラメータを追加しました。

   バージョン 3.13.3 で変更: "name" と "context" を含む任意の追加パラ
   メーターを渡す "kwargs" が追加されました。

   バージョン 3.13.4 で変更: *name* と、値が "None" の場合の *context*
   を渡す変更を取り消しました。 それ以外の任意のキーワード引数は、
   3.13.3 との後方互換性を保つため、引き続き渡されます。

   バージョン 3.14 で変更: すべての *kwargs* を渡すようになりました。
   *eager_start* パラメーターは、タスクを即座に実行するファクトリーで
   機能します。

loop.set_task_factory(factory)

   "loop.create_task()" が使用するタスクファクトリーを設定します。

   *factory* が "None" の場合は、デフォルトのタスクファクトリーを設定
   します。 それ以外の場合、*factory* はシグネチャが "(loop, coro,
   **kwargs)" に対応する *呼び出し可能オブジェクト* でなければなりませ
   ん。 ここで *loop* は使用するイベントループへの参照、*coro* はコル
   ーチンオブジェクトです。 この呼び出し可能オブジェクトは、すべての
   *kwargs* を呼び出し先に渡し、"asyncio.Task" と互換性のあるオブジェ
   クトを返す必要があります。

   バージョン 3.13.3 で変更: すべての *kwargs* を "asyncio.Task" に渡
   すことが必須になりました。

   バージョン 3.13.4 で変更: *name* をタスクファクトリーに渡さなくなり
   ました。 *context* も、値が "None" の場合は渡さなくなりました。

   バージョン 3.14 で変更: *name* と *context* を、再び無条件にタスク
   ファクトリーに渡すようになりました。

loop.get_task_factory()

   タスクファクトリを返します。デフォルトのタスクファクトリを使用中の
   場合は "None" を返します。


ネットワーク接続の確立
----------------------

async loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, happy_eyeballs_delay=None, interleave=None, all_errors=False)

   *host* と *port* で指定されたアドレスとのストリーミングトランスポー
   ト接続をオープンします。

   ソケットファミリーは *host* (または *family* 引数が与えられた場合は
   *family*) に依存し、 "AF_INET" か "AF_INET6" のいずれかを指定します
   。

   ソケットタイプは "SOCK_STREAM" になります。

   *protocol_factory* は asyncio プロトコル の実装を返す呼び出し可能オ
   ブジェクトでなければなりません。

   このメソッドはバックグラウンドで接続の確立を試みます。成功した場合
   、メソッドは "(transport, protocol)" のペアを返します。

   時系列での下層処理の概要は以下のとおりです:

   1. 接続を確立し、その接続に対する トランスポート が生成されます。

   2. *protocol_factory* が引数なしで呼び出され、ファクトリが プロトコ
      ル インスタンスを返すよう要求します。

   3. プロトコルインスタンスが "connection_made()" メソッドを呼び出す
      ことにより、トランスポートと紐付けられます。

   4. 成功すると "(transport, protocol)" タプルが返されます。

   作成されたトランスポートは実装依存の双方向ストリームです。

   その他の引数:

   * *ssl*: 偽値以外が与えられた場合、SSL/TLS トランスポートが作成され
     ます (デフォルトでは暗号化なしの TCP トランスポートが作成されます
     )。 *ssl* が "ssl.SSLContext" オブジェクトの場合、このコンテキス
     トがトランスポートを作成するために使用されます; *ssl* が "True"
     の場合、 "ssl.create_default_context()" が返すデフォルトのコンテ
     キストが使われます。

     参考: SSL/TLS セキュリティについての考察

   * *server_hostname* は対象サーバーの証明書との一致を確認するための
     ホスト名を設定または上書きします。この引数は *ssl* が "None" でな
     い場合のみ設定すべきです。デフォルトでは *host* に指定したサーバ
     ー名が使用されます。 *host* が空の文字列の場合のデフォルト値は設
     定されていません。その場合、 *server_hostname* を必ず指定してくだ
     さい。 *server_hostname* も空の文字列の場合は、ホスト名の一致確認
     は行われません (これは深刻なセキュリティリスクであり、中間者攻撃
     を受ける可能性があります)。

   * *family*、*proto*、*flags* は省略可能な引数で、それぞれアドレスフ
     ァミリー、プロトコル、フラグを指定します。 *host* の名前解決のた
     めに "getaddrinfo()" に渡されます。 指定する場合は、"socket" モジ
     ュールの対応する整数定数を使ってください。

   * *happy_eyeballs_delay* が設定されると、この接続に対して Happy
     Eyeballs が有効化されます。設定する値は浮動小数点数であり、次の接
     続試行を開始する前に、現在の接続試行が完了するのを待つ時間を秒単
     位で表現します。この値は **RFC 8305** で定義されている "接続試行
     遅延" に相当します。RFC で推奨されている実用的なデフォルト値は
     "0.25" (250 ミリ秒) です。

   * *interleave* はホスト名が複数の IP アドレスに名前解決される場合の
     アドレスの並べ替えを制御します。 "0" または未指定の場合並べ替えは
     行われず、 "getaddrinfo()" が返す順番にしたがってアドレスへの接続
     を試行します。正の整数が指定されると、アドレスはアドレスファミリ
     に応じてインターリーブされます。このとき、与えられた整数は **RFC
     8305** で定義される "最初のアドレスファミリカウント (First
     Address Family Count)" として解釈されます。デフォルト値は、
     *happy_eyeballs_delay* が指定されない場合は "0" であり、指定され
     た場合は "1" です。

   * *sock* を与える場合、トランスポートに使用される、既存の、かつ接続
     済の "socket.socket" オブジェクトを指定します。*sock* を指定する
     場合、*host*、 *port*、 *family*、 *proto*、 *flags*、
     *happy_eyeballs_delay*、 *interleave* および *local_addr* のいず
     れも指定してはいけません。

     注釈:

       *sock* 引数を渡すと、ソケットの所有権は作成されたトランスポート
       に移ります。 ソケットを閉じるには、トランスポートの "close()"
       メソッドを呼び出してください。

   * *local_addr* を与える場合、ソケットをローカルにバインドするために
     使用する "(local_host, local_port)" タプルを指定します。
     *local_host* と *local_port* は、 *host* および *port* と同じく
     "getaddrinfo()" を使ってルックアップされます。

   * *ssl_handshake_timeout* は TLS ハンドシェイクが完了するまでの
     (TLS 接続のための) 待ち時間を秒単位で指定します。指定した待ち時間
     を超えると接続は中断します。 "None" が与えられた場合はデフォルト
     値 "60.0" が使われます。

   * *ssl_shutdown_timeout* は、接続を中断する前に SSL の終了処理を待
     つ時間を秒単位で指定します。 "None" (デフォルト) の場合は "30.0"
     秒です。

   * *all_errors* は、接続を確立できなかった場合に送出する例外を指定し
     ます。 デフォルトでは "Exception" を一つだけ送出します。 例外が一
     つだけの場合や、すべてのエラーメッセージが同じ場合は最初の例外、
     それ以外の場合はエラーメッセージをまとめた一つの "OSError" です。
     "all_errors" が "True" の場合、すべての例外を含む
     "ExceptionGroup" を送出します (例外が一つだけでも同じです)。

   バージョン 3.5 で変更: "ProactorEventLoop" において SSL/TLS のサポ
   ートが追加されました。

   バージョン 3.6 で変更: 全ての TCP 接続に対してデフォルトでソケット
   オプション socket.TCP_NODELAY  が設定されるようになりました。

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

   バージョン 3.8 で変更: *happy_eyeballs_delay* と *interleave* が追
   加されました。Happy Eyeballs アルゴリズム: デュアルスタックホストで
   の接続を成功させる方法。 サーバーへの IPv4 の経路とプロトコルが機能
   していても、IPv6 の経路とプロトコルが機能していないと、デュアルスタ
   ックのクライアントアプリケーションでは、IPv4 専用のクライアントに比
   べて接続が大幅に遅れることがあります。 デュアルスタックのクライアン
   トで利用者の使い勝手が悪くなるため、これは望ましくありません。 この
   文書は、利用者に見えるこの遅延を減らすアルゴリズムの要件を定め、一
   つのアルゴリズムを示します。詳しくは右記を参照してください:
   https://datatracker.ietf.org/doc/html/rfc6555

   バージョン 3.11 で変更: *ssl_shutdown_timeout* パラメータが追加され
   ました。

   バージョン 3.12 で変更: *all_errors* が追加されました

   バージョン 3.15 で変更: "ssl.check_hostname" が "True" で、
   "server_hostname" が指定されていない場合に "ValueError" を送出する
   ようになりました。

   参考:

     "open_connection()" 関数は高水準の代替 API です。この関数は
     ("StreamReader", "StreamWriter") のペアを返し、 async/await コー
     ドから直接使うことができます。

async loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_port=None, allow_broadcast=None, sock=None)

   データグラム接続 (UDP) を生成します。

   ソケットファミリーは *host* (または *family* 引数が与えられた場合は
   *family*) に依存し、 "AF_INET"、 "AF_INET6"、"AF_UNIX" のいずれかを
   指定します。

   ソケットタイプは "SOCK_DGRAM" になります。

   *protocol_factory* は asyncio プロトコル の実装を返す呼び出し可能オ
   ブジェクトでなければなりません。

   成功すると "(transport, protocol)" タプルが返されます。

   その他の引数:

   * *local_addr* が指定される場合、ソケットをローカルにバインドするた
     めの "(local_host, local_port)" のタプルを指定します。
     *local_host* と *local_port* は "getaddrinfo()" メソッドを使用し
     て検索されます。

     注釈:

       Windows で Proactor イベントループを使い、"local_addr=None" と
       して実行すると、"errno.WSAEINVAL" の "OSError" が送出されます。

   * *remote_addr* を指定する場合は、ソケットをリモートアドレスに接続
     するための "(remote_host, remote_port)" タプルを渡します。
     *remote_host* と *remote_port* は "getaddrinfo()" を使って検索さ
     れます。

   * *family*、*proto*、*flags* は省略可能な引数で、それぞれアドレスフ
     ァミリー、プロトコル、フラグを指定します。 *host* の名前解決のた
     めに "getaddrinfo()" に渡されます。 指定する場合は、"socket" モジ
     ュールの対応する整数定数を使ってください。

   * *reuse_port* は、同じポートにバインドされた既存の端点すべてがこの
     フラグを設定して生成されている場合に限り、この端点を既存の端点と
     同じポートにバインドすることを許可するよう、カーネルに指示します
     （訳註: ソケットのオプション SO_REUSEPORT を使用します）。このオ
     プションは、Windows やいくつかの UNIX システムではサポートされて
     いません。socket.SO_REUSEPORT  定数が定義されていなければ、この機
     能はサポートされません。

   * *allow_broadcast* は、カーネルに、このエンドポイントがブロードキ
     ャストアドレスにメッセージを送信することを許可するように指示しま
     す。

   * オプションの *sock* を指定することで、既存の、すでに接続されてい
     る "socket.socket" をトランスポートで使用することができます。この
     オプションを使用する場合、*local_addr* と *remote_addr* は省略し
     てください ("None" でなければなりません)。

     注釈:

       *sock* 引数を渡すと、ソケットの所有権は作成されたトランスポート
       に移ります。 ソケットを閉じるには、トランスポートの "close()"
       メソッドを呼び出してください。

   UDP echo クライアントプロトコル および UDP echo サーバープロトコル
   の例を参照してください。

   バージョン 3.4.4 で変更: *family*, *proto*, *flags*,
   *reuse_address*, *reuse_port*, *allow_broadcast*, *sock* パラメータ
   が追加されました。

   バージョン 3.8 で変更: Windows サポートが追加されました。

   バージョン 3.8.1 で変更: socket.SO_REUSEADDR の利用が UDP に対して
   重大なセキュリティ上の懸念をもたらすため、 *reuse_address* パラメー
   タはサポートされなくなりました。明示的に "reuse_address=True" を設
   定すると例外を送出します。"SO_REUSEADDR" を使って、同一の UDP ソケ
   ットアドレスに対して複数のプロセスが異なる UID でソケットを割り当て
   ている場合、受信パケットは複数のソケット間にランダムに分散する可能
   性があります。サポートされているプラットフォームでは、 *reuse_port*
   が同様の機能に対する代用品として利用できます。 *reuse_port* は代替
   機能として socket.SO_REUSEPORT を使っており、複数のプロセスが異なる
   UID で同一のソケットに対して割り当てられるのを明確に禁止します。

   バージョン 3.11 で変更: Python 3.8.1、3.7.6、3.6.10 以降で無効にな
   っていた *reuse_address* パラメーターが、完全に削除されました。

async loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)

   Unix 接続を生成します。

   ソケットファミリーは "AF_UNIX" になります; また、ソケットタイプは
   "SOCK_STREAM" になります。

   成功すると "(transport, protocol)" タプルが返されます。

   *path* は Unix ドメインソケット名で、 *sock* パラメータが指定されな
   い場合は必須です。 抽象 Unix ソケット、 "str"、 "bytes"、 and
   "Path" 形式でのパスがサポートされています。

   このメソッドの引数についての詳細は "loop.create_connection()" メソ
   ッドのドキュメントを参照してください。

   利用可能な環境: Unix.

   バージョン 3.7 で変更: *ssl_handshake_timeout* パラメーターが追加さ
   れました。 *path* パラメーターに *path-like object* を指定できるよ
   うになりました。

   バージョン 3.11 で変更: *ssl_shutdown_timeout* パラメータが追加され
   ました。


ネットワークサーバの生成
------------------------

async loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, keep_alive=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True)

   アドレス *host* のポート *port* で接続を待ち受ける TCP サーバー (ソ
   ケットタイプ "SOCK_STREAM") を作成します。

   "Server" オブジェクトを返します。

   引数:

   * *protocol_factory* は asyncio プロトコル の実装を返す呼び出し可能
     オブジェクトでなければなりません。

   * *host* パラメータはいくつかの方法で指定することができ、その値によ
     ってサーバーがどこをリッスンするかが決まります。

     * *host* が文字列の場合、 TCP サーバーは *host* で指定した単一の
       ネットワークインターフェースに束縛されます。

     * *host* が文字列のシーケンスである場合、 TCP サーバーはそのシー
       ケンスで指定された全てのネットワークインターフェースに束縛され
       ます。

     * *host* が空の文字列か "None" の場合、すべてのインターフェースが
       想定され、複合的なソケットのリスト (通常は一つが IPv4、もう一つ
       が IPv6) が返されます。

   * *port* パラメーターには、サーバーが待ち受けるポートを指定できます
     。 "0" または "None" (デフォルト) の場合、未使用のポートがランダ
     ムに選ばれます。 *host* が複数のネットワークインターフェースに解
     決される場合は、インターフェースごとに異なるランダムなポートが選
     ばれることに注意してください。

   * *family* に "socket.AF_INET" または "AF_INET6" を指定すると、ソケ
     ットでそれぞれ IPv4 または IPv6 を使うよう強制できます。 指定しな
     ければホスト名から決まり、デフォルト値は "AF_UNSPEC" です。

   * *flags* は "getaddrinfo()" のためのビットマスクになります。

   * サーバーで既存のソケットオブジェクトを使用するために、オプション
     の引数 *sock* にソケットオブジェクトを設定することができます。指
     定した場合、 *host* と *port* を指定してはいけません。

     注釈:

       *sock* 引数を渡すと、ソケットの所有権は作成されたサーバーに移り
       ます。 ソケットを閉じるには、サーバーの "close()" メソッドを呼
       び出してください。

   * *backlog* は "listen()" に渡される、キューに入るコネクションの最
     大数になります (デフォルトは 100)。

   * 確立した接続の上で TLS を有効化するために、 *ssl* に "SSLContext"
     のインスタンスを指定することができます。

   * *reuse_address* は、"TIME_WAIT" 状態にあるローカルソケットを、そ
     の状態が自然にタイムアウトするのを待つことなく再利用するようカー
     ネルに指示します（訳註: ソケットのオプション SO_REUSEADDR を使用
     します）。指定しない場合、UNIX では自動的に "True" が設定されます
     。

   * *reuse_port* は、同じポートにバインドされた既存の端点すべてがこの
     フラグを設定して生成されている場合に限り、この端点を既存の端点と
     同じポートにバインドすることを許可するよう、カーネルに指示します
     （訳註: ソケットのオプション SO_REUSEPORT を使用します）。このオ
     プションは、Windows ではサポートされていません。

   * *keep_alive* を "True" にすると、定期的なメッセージ送信を有効にし
     て接続を維持します。

   バージョン 3.13 で変更: *keep_alive* パラメーターが追加されました。

   * *ssl_handshake_timeout* は TLS ハンドシェイクが完了するまでの
     (TLS サーバーのための) 待ち時間を秒単位で指定します。指定した待ち
     時間を超えると接続は中断します。 "None" が与えられた場合はデフォ
     ルト値 "60.0" が使われます。

   * *ssl_shutdown_timeout* は、接続を中断する前に SSL の終了処理を待
     つ時間を秒単位で指定します。 "None" (デフォルト) の場合は "30.0"
     秒です。

   * *start_serving* が "True" に設定された場合 (これがデフォルトです)
     、 生成されたサーバーは即座に接続の受け付けを開始します。 "False"
     が指定された場合、ユーザーは接続の受け付けを開始するために
     "Server.start_serving()" または "Server.serve_forever()" を待ち受
     け (await) る必要があります。

   バージョン 3.5 で変更: "ProactorEventLoop" において SSL/TLS のサポ
   ートが追加されました。

   バージョン 3.5.1 で変更: *host* パラメータに文字列のシーケンスを指
   定できるようになりました。

   バージョン 3.6 で変更: *ssl_handshake_timeout* と *start_serving*
   パラメーターが追加されました。 すべての TCP 接続で、デフォルトでソ
   ケットオプション socket.TCP_NODELAY を設定するようになりました。

   バージョン 3.11 で変更: *ssl_shutdown_timeout* パラメータが追加され
   ました。

   参考:

     "start_server()" 関数は高水準の代替 API です。この関数は
     "StreamReader" と "StreamWriter" のペアを返し、async/await コード
     から使うことができます。

async loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None, start_serving=True, cleanup_socket=True)

   "loop.create_server()" と似ていますが、ソケットファミリー "AF_UNIX"
   を使います。

   *path* は Unix ドメインソケット名で、 *sock* パラメータが指定されな
   い場合は必須です。 抽象 Unix ソケット、 "str"、 "bytes"、 and
   "Path" 形式でのパスがサポートされています。

   *cleanup_socket* が真の場合、サーバーを閉じると Unix ソケットをファ
   イルシステムから自動的に削除します。 ただし、サーバーを作成した後に
   ソケットが置き換えられていれば削除しません。

   このメソッドの引数についての詳細は "loop.create_server()" メソッド
   のドキュメントを参照してください。

   利用可能な環境: Unix.

   バージョン 3.7 で変更: *ssl_handshake_timeout* と *start_serving*
   パラメーターが追加されました。 *path* パラメーターに "Path" オブジ
   ェクトを指定できるようになりました。

   バージョン 3.11 で変更: *ssl_shutdown_timeout* パラメータが追加され
   ました。

   バージョン 3.13 で変更: *cleanup_socket* パラメーターが追加されまし
   た。

async loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)

   すでに確立した接続を transport と protocol のペアでラップします。

   このメソッドは asyncio の範囲外で確立された接続を使うサーバーに対し
   ても使えますが、その場合でも接続は asyncio を使って処理されます。

   引数:

   * *protocol_factory* は asyncio プロトコル の実装を返す呼び出し可能
     オブジェクトでなければなりません。

   * *sock* は "socket.accept" メソッドが返す既存のソケットオブジェク
     トです。

     注釈:

       *sock* 引数を渡すと、ソケットの所有権は作成されたトランスポート
       に移ります。 ソケットを閉じるには、トランスポートの "close()"
       メソッドを呼び出してください。

   * *ssl* には "SSLContext" を指定できます。指定すると、受け付けたコ
     ネクション上での SSL を有効にします。

   * *ssl_handshake_timeout* は SSL ハンドシェイクが完了するまでの
     (SSL 接続のための) 待ち時間を秒単位で指定します。 "None" が与えら
     れた場合はデフォルト値 "60.0" が使われます。

   * *ssl_shutdown_timeout* は、接続を中断する前に SSL の終了処理を待
     つ時間を秒単位で指定します。 "None" (デフォルト) の場合は "30.0"
     秒です。

   "(transport, protocol)" のペアを返します。

   Added in version 3.5.3.

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

   バージョン 3.11 で変更: *ssl_shutdown_timeout* パラメータが追加され
   ました。


ファイルの転送
--------------

async loop.sendfile(transport, file, offset=0, count=None, *, fallback=True)

   *transport* を通じて *file* を送信します。送信したデータの総バイト
   数を返します。

   このメソッドは、もし利用可能であれば高性能な "os.sendfile()" を利用
   します。

   *file* はバイナリモードでオープンされた通常のファイルオブジェクトで
   なければなりません。

   *offset* はファイルの読み込み開始位置を指定します。 *count* が指定
   された場合、ファイルの EOF までファイルを送信する代わりに、 *count*
   で指定された総バイト数の分だけ送信します。ファイルオブジェクトが指
   し示す位置は、メソッドがエラーを送出した場合でも更新されます。この
   場合実際に送信されたバイト数は "file.tell()" メソッドで取得すること
   ができます。

   *fallback* を "True" に指定することで、 asyncio がプラットフォーム
   が sendfile システムコールをサポートしていない場合 (たとえば
   Windows や Unix の SSL ソケットなど) に別の方法でファイルの読み込み
   と送信を行うようにすることができます。

   システムが *sendfile* システムコールをサポートしておらず、かつ
   *fallback* が "False" の場合、 "SendfileNotAvailableError" 例外を送
   出します。

   Added in version 3.7.


TLS へのアップグレード
----------------------

async loop.start_tls(transport, protocol, sslcontext, *, server_side=False, server_hostname=None, ssl_handshake_timeout=None, ssl_shutdown_timeout=None)

   既存のトランスポートベースの接続を TLS にアップグレードします。

   TLS のエンコード・デコードを行うインスタンスを作成し、*transport*
   と *protocol* の間に挿入します。 このインスタンスは、*transport* に
   対するプロトコルと、*protocol* に対するトランスポートの両方のインタ
   ーフェースを実装します。

   作成した、二つのインターフェースを持つインスタンスを返します。
   *await* の後、*protocol* は元の *transport* の使用をやめ、返された
   オブジェクトだけを使って通信する必要があります。 エンコーダーが
   *protocol* 側のデータをキャッシュし、必要に応じて追加の TLS セッシ
   ョンパケットを *transport* と交換するためです。

   渡されたトランスポートがすでに終了処理中である場合など、状況によっ
   ては "None" を返すことがあります。

   引数:

   * *transport* と *protocol* には、 "create_server()" や
     "create_connection()" が返すものと同等のインスタンスを指定します
     。

   * *sslcontext*: 構成済みの "SSLContext" インスタンスです。

   * ("create_server()" で生成されたような) サーバーサイドの接続をアッ
     プグレードする場合は *server_side* に "True" を渡します。

   * *server_hostname*: 対象のサーバーの証明書との照合に使われるホスト
     名を設定または上書きします。

   * *ssl_handshake_timeout* は TLS ハンドシェイクが完了するまでの
     (TLS 接続のための) 待ち時間を秒単位で指定します。指定した待ち時間
     を超えると接続は中断します。 "None" が与えられた場合はデフォルト
     値 "60.0" が使われます。

   * *ssl_shutdown_timeout* は、接続を中断する前に SSL の終了処理を待
     つ時間を秒単位で指定します。 "None" (デフォルト) の場合は "30.0"
     秒です。

   Added in version 3.7.

   バージョン 3.11 で変更: *ssl_shutdown_timeout* パラメータが追加され
   ました。


ファイル記述子の監視
--------------------

loop.add_reader(fd, callback, *args)

   ファイル記述子 *fd* に対する読み込みが可能かどうかの監視を開始し、
   *fd* が読み込み可能になると、指定した引数でコールバック *callback*
   を呼び出します。

   *fd* にすでに登録されているコールバックがあれば、キャンセルして
   *callback* に置き換えます。

loop.remove_reader(fd)

   ファイル記述子 *fd* が読み込み可能になるのを監視する処理を停止しま
   す。 それまで *fd* の読み込みを監視していた場合は "True" を返します
   。

loop.add_writer(fd, callback, *args)

   ファイル記述子 *fd* が書き込み可能になるのを監視し始めます。 書き込
   み可能になると、指定した引数 *args* で *callback* を呼び出します。

   *fd* にすでに登録されているコールバックがあれば、キャンセルして
   *callback* に置き換えます。

   コールバック *callback* に キーワード引数を渡す 場合は
   "functools.partial()" を使ってください。

loop.remove_writer(fd)

   ファイル記述子 *fd* が書き込み可能になるのを監視する処理を停止しま
   す。 それまで *fd* の書き込みを監視していた場合は "True" を返します
   。

これらのメソッドに対する制限事項については プラットフォームのサポート
状況 節も参照してください。


ソケットオブジェクトと直接やりとりする
--------------------------------------

一般に、 "loop.create_connection()" や "loop.create_server()" のような
トランスポートベースの API を使ったプロトコルの実装はソケットと直接や
り取りする実装に比べて高速です。しかしながら、パフォーマンスが重要でな
く、直接 "socket" オブジェクトとやりとりした方が便利なユースケースがい
くつかあります。

async loop.sock_recv(sock, nbytes)

   *nbytes* で指定したバイト数までのデータをソケット *sock* から受信し
   ます。 このメソッドは "socket.recv()" の非同期版です。

   受信したデータをバイトオブジェクトとして返します。

   *sock* はノンブロッキングソケットでなければなりません。

   バージョン 3.7 で変更: このメソッドは常にコルーチンメソッドとしてド
   キュメントに記載されてきましたが、 Python 3.7 より前のリリースでは
   "Future" オブジェクトを返していました。 Python 3.7 からは "async
   def" メソッドになりました。

async loop.sock_recv_into(sock, buf)

   ソケット *sock* からデータを受信してバッファ *buf* に格納します。ブ
   ロッキングコードの "socket.recv_into()" メソッドをモデルとしていま
   す。

   バッファに書き込んだデータのバイト数を返します。

   *sock* はノンブロッキングソケットでなければなりません。

   Added in version 3.7.

async loop.sock_recvfrom(sock, bufsize)

   *sock* から最大 *bufsize* バイトのデータグラムを受信します。
   "socket.recvfrom()" の非同期版です。

   (受信したデータ, 送信元アドレス) のタプルを返します。

   *sock* はノンブロッキングソケットでなければなりません。

   Added in version 3.11.

async loop.sock_recvfrom_into(sock, buf, nbytes=0)

   *sock* から最大 *nbytes* バイトのデータグラムを受信し、*buf* に格納
   します。 "socket.recvfrom_into()" の非同期版です。

   (受信したバイト数, 送信元アドレス) のタプルを返します。

   *sock* はノンブロッキングソケットでなければなりません。

   Added in version 3.11.

async loop.sock_sendall(sock, data)

   データ *data* をソケット *sock* に送信します。 "socket.sendall()"
   メソッドの非同期版です。

   このメソッドは *data* をすべて送信し終えるか、またはエラーが起きる
   までデータをソケットに送信し続けます。送信に成功した場合 "None" を
   返します。エラーの場合は例外が送出されます。エラーとなった場合、接
   続の受信側で正しく処理されたデータの総量を特定する方法はありません
   。

   *sock* はノンブロッキングソケットでなければなりません。

   バージョン 3.7 で変更: このメソッドは常にコルーチンメソッドとしてド
   キュメントに記載されてきましたが、 Python 3.7 より前のリリースでは
   "Future" オブジェクトを返していました。 Python 3.7 からは "async
   def" メソッドになりました。

async loop.sock_sendto(sock, data, address)

   *sock* から *address* にデータグラムを送信します。
   "socket.sendto()" の非同期版です。

   送信したバイト数を返します。

   *sock* はノンブロッキングソケットでなければなりません。

   Added in version 3.11.

async loop.sock_connect(sock, address)

   ソケット *sock* をアドレス *address* のリモートソケットに接続します
   。

   "socket.connect()" の非同期版です。

   *sock* はノンブロッキングソケットでなければなりません。

   "SelectorEventLoop" では、*address* を事前に名前解決する必要はあり
   ません。 "AF_INET" と "AF_INET6" のソケットでは、"sock_connect" は
   まず "socket.inet_pton()" で *address* が解決済みかどうかを確認し、
   未解決なら "loop.getaddrinfo()" で解決します。

   Windows のデフォルトである "ProactorEventLoop" は、*address* の名前
   解決を行いません。 ホストは数値の IP アドレスでなければならず、ホス
   ト名を渡すと "OSError" を送出します。 先に "loop.getaddrinfo()" で
   アドレスを解決するか、すべてのプラットフォームで名前解決を行う
   "loop.create_connection()" を使ってください。

   バージョン 3.5.2 で変更: "SelectorEventLoop" では、"address" を事前
   に名前解決する必要がなくなりました。

   参考: "loop.create_connection()" および  "asyncio.open_connection()"。

async loop.sock_accept(sock)

   接続を受け付けます。ブロッキングコールの "socket.accept()" メソッド
   をモデルとしています。

   ソケットはアドレスに束縛済みで、接続を listen 中である必要がありま
   す。戻り値は "(conn, address)" のペアで、*conn* は接続を通じてデー
   タの送受信を行うための *新しい* ソケットオブジェクト、*address* は
   接続先の端点でソケットに束縛されているアドレスを示します。

   *sock* はノンブロッキングソケットでなければなりません。

   バージョン 3.7 で変更: このメソッドは常にコルーチンメソッドとしてド
   キュメントに記載されてきましたが、 Python 3.7 より前のリリースでは
   "Future" オブジェクトを返していました。 Python 3.7 からは "async
   def" メソッドになりました。

   参考: "loop.create_server()" および "start_server()"。

async loop.sock_sendfile(sock, file, offset=0, count=None, *, fallback=True)

   ファイルを送信します。利用可能なら高性能な "os.sendfile" を使います
   。送信したデータの総バイト数を返します。

   "socket.sendfile()" メソッドの非同期版です。

   *sock* は "socket.SOCK_STREAM" タイプのノンブロッキングな "socket"
   でなければなりません。

   *file* はバイナリモードでオープンされた通常のファイルオブジェクトで
   なければなりません。

   *offset* はファイルの読み込み開始位置を指定します。 *count* が指定
   された場合、ファイルの EOF までファイルを送信する代わりに、 *count*
   で指定された総バイト数の分だけ送信します。ファイルオブジェクトが指
   し示す位置は、メソッドがエラーを送出した場合でも更新されます。この
   場合実際に送信されたバイト数は "file.tell()" メソッドで取得すること
   ができます。

   *fallback* が "True" に設定された場合、 プラットフォームが sendfile
   システムコールをサポートしていない場合 (たとえば Windows や Unix の
   SSL ソケットなど) に asyncio が別の方法でファイルの読み込みと送信を
   行うようにすることができます。

   システムが *sendfile* システムコールをサポートしておらず、かつ
   *fallback* が "False" の場合、 "SendfileNotAvailableError" 例外を送
   出します。

   *sock* はノンブロッキングソケットでなければなりません。

   Added in version 3.7.


DNS
---

async loop.getaddrinfo(host, port, *, family=0, type=0, proto=0, flags=0)

   "socket.getaddrinfo()" の非同期版です。

async loop.getnameinfo(sockaddr, flags=0)

   "socket.getnameinfo()" の非同期版です。

注釈:

  *getaddrinfo* と *getnameinfo* は、内部ではループのデフォルトのスレ
  ッドプール Executor を通じて同期版を呼び出します。 この Executor に
  空きがなくなると処理が遅れ、高水準のネットワークライブラリでタイムア
  ウトが増えることがあります。 これを軽減するには、他のユーザー処理に
  別の Executor を使うか、ワーカー数の多い Executor をデフォルトに設定
  することを検討してください。

バージョン 3.7 で変更: *getaddrinfo* と *getnameinfo* の2つのメソッド
は、いずれも常にコルーチンメソッドとしてドキュメントに記載されてきまし
たが、 Python 3.7 より前のリリースでは、実際には "asyncio.Future" オブ
ジェクトを返していました。 Python 3.7 からはどちらのメソッドもコルーチ
ンになりました。


パイプとやりとりする
--------------------

async loop.connect_read_pipe(protocol_factory, pipe)

   *pipe* の読み込み側の終端をイベントループに登録します。

   *protocol_factory* は asyncio プロトコル の実装を返す呼び出し可能オ
   ブジェクトでなければなりません。

   *pipe* は *ファイルライクオブジェクト* です。 指定できるオブジェク
   トについては サポートされるパイプオブジェクト を参照してください。

   "(transport, protocol)" のペアを返します。ここで *transport* は
   "ReadTransport" のインターフェースをサポートし、 *protocol* は
   *protocol_factory* ファクトリでインスタンス化されたオブジェクトです
   。

   "SelectorEventLoop" イベントループでは、*pipe* が非ブロックモードに
   設定されます。

async loop.connect_write_pipe(protocol_factory, pipe)

   *pipe* の書き込み側終端をイベントループに登録します。

   *protocol_factory* は asyncio プロトコル の実装を返す呼び出し可能オ
   ブジェクトでなければなりません。

   *pipe* は *ファイルライクオブジェクト* です。 指定できるオブジェク
   トについては サポートされるパイプオブジェクト を参照してください。

   "(transport, protocol)" のペアを返します。ここで *transport* は
   "WriteTransport" のインスタンスであり、 *protocol* は
   *protocol_factory* ファクトリでインスタンス化されたオブジェクトです
   。

   "SelectorEventLoop" イベントループでは、*pipe* が非ブロックモードに
   設定されます。

-[ サポートされるパイプオブジェクト ]-

これらのメソッドで扱えるのは、OS が読み書きの準備状況をポーリングでき
るオブジェクト、またはオーバーラップ I/O を実行できるオブジェクトだけ
です。 ディスク上の通常のファイルは、どのプラットフォームでも **サポー
トされません**。 asyncio に非同期ファイル I/O はないため、イベントルー
プをブロックせずに通常のファイルを読み書きするには
"loop.run_in_executor()" を使ってください。

Unix の "SelectorEventLoop" では、*pipe* は次のいずれかをラップする必
要があります。

* "os.pipe()" で作成したペアの一端や、"os.mkfifo()" で作成した FIFO な
  どのパイプ

* ソケット

* 端末などのキャラクターデバイス

Windows では、これらのメソッドを実装しているのは "ProactorEventLoop"
だけです。 ハンドルを I/O 完了ポートに関連付ける必要があるため、*pipe*
はオーバーラップ I/O 用に開かれたハンドル ("FILE_FLAG_OVERLAPPED" フラ
グで作成したもの) をラップしなければなりません。 オーバーラップ I/O 用
に開かれていないハンドルは拒否されます。 特に、標準ストリーム
("sys.stdin"、"sys.stdout"、"sys.stderr")、コンソールのハンドル、
"os.pipe()" で作成したパイプは、オーバーラップ I/O 用には開かれていな
いため、これらのメソッドでは **使えません**。

注釈:

  "SelectorEventLoop" は Windows 上で上記のメソッドをサポートしていま
  せん。 Windowsでは代わりに "ProactorEventLoop" を使ってください。

参考: "loop.subprocess_exec()" および "loop.subprocess_shell()" メソッド。


Unix シグナル
-------------

loop.add_signal_handler(signum, callback, *args)

   *callback* をシグナル *signum* のハンドラーに設定し、*args* を位置
   引数として渡します。

   このコールバックは、キューに入っている他のコールバックや実行可能な
   コルーチンとともに、イベントループ *loop* によって呼び出されます。
   "signal.signal()" で登録したシグナルハンドラとは異なり、この関数で
   登録したコールバックはイベントループとやりとりできます。

   シグナルナンバーが誤っているか捕捉不可能な場合 "ValueError" が送出
   されます。ハンドラーの設定に問題があった場合 "RuntimeError" が送出
   されます。

   コールバック *callback* に キーワード引数を渡す 場合は
   "functools.partial()" を使ってください。

   "signal.signal()" と同じく、この関数はメインスレッドから呼び出され
   なければなりません。

loop.remove_signal_handler(sig)

   シグナル *sig* に対するハンドラを削除します。

   シグナルハンドラが削除された場合 "True" を返します。シグナルに対し
   てハンドラが設定されていない場合には "False" を返します。

   利用可能な環境: Unix.

参考: "signal" モジュール。


スレッドまたはプロセスプールでコードを実行する
----------------------------------------------

awaitable loop.run_in_executor(executor, func, *args)

   指定した Executor 内で、*args* を位置引数として *func* を呼び出すよ
   うに手配します。

   *executor* 引数には "concurrent.futures.Executor" のインスタンスを
   指定します。 "None" の場合はデフォルトの Executor を使います。 デフ
   ォルトの Executor は "loop.set_default_executor()" で設定できます。
   設定しなければ、"run_in_executor()" が必要になった時点で
   "concurrent.futures.ThreadPoolExecutor" を初期化して使います。

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

      import asyncio
      import concurrent.futures

      def blocking_io():
          # ログ記録などのファイル操作はイベントループをブロックする
          # 可能性があるので、スレッドプールで実行する。
          with open('/dev/urandom', 'rb') as f:
              return f.read(100)

      def cpu_bound():
          # CPU バウンドな処理はイベントループをブロックする。
          # 通常はプロセスプールで実行する方がよい。
          return sum(i * i for i in range(10 ** 7))

      async def main():
          loop = asyncio.get_running_loop()

          ## 実行方法:

          # 1. ループのデフォルトの Executor で実行する:
          result = await loop.run_in_executor(
              None, blocking_io)
          print('default thread pool', result)

          # 2. 自分で作成したスレッドプールで実行する:
          with concurrent.futures.ThreadPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, blocking_io)
              print('custom thread pool', result)

          # 3. 自分で作成したプロセスプールで実行する:
          with concurrent.futures.ProcessPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, cpu_bound)
              print('custom process pool', result)

          # 4. 自分で作成したインタープリタプールで実行する:
          with concurrent.futures.InterpreterPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, cpu_bound)
              print('custom interpreter pool', result)

      if __name__ == '__main__':
          asyncio.run(main())

   例の 3 番では、"ProcessPoolExecutor" が使う "multiprocessing" の特
   性により、エントリーポイントのガード ("if __name__ == '__main__'")
   が必要です。 メインモジュールの安全なインポート を参照してください
   。

   このメソッドは "asyncio.Future" オブジェクトを返します。

   関数 *func* に キーワード引数を渡す 場合は "functools.partial()" を
   使ってください。

   バージョン 3.5.3 で変更: "loop.run_in_executor()" は内部で生成する
   スレッドプールエグゼキュータの "max_workers" を設定せず、代わりにス
   レッドプールエグゼキュータ ("ThreadPoolExecutor") にデフォルト値を
   設定させるようになりました。

loop.set_default_executor(executor)

   *executor* を、"run_in_executor()" が使うデフォルトの Executor に設
   定します。 *executor* は "ThreadPoolExecutor" のインスタンスでなけ
   ればなりません。 これには "InterpreterPoolExecutor" も含まれます。

   バージョン 3.11 で変更: *executor* は "ThreadPoolExecutor" のインス
   タンスでなければならなくなりました。


エラー処理 API
--------------

イベントループ内での例外の扱い方をカスタマイズできます。

loop.set_exception_handler(handler)

   *handler* を新しいイベントループ例外ハンドラーとして設定します。

   *handler* が "None" の場合、デフォルトの例外ハンドラが設定されます
   。そうでなければ、 *handler* は "(loop, context)" に一致する関数シ
   グネチャを持った呼び出し可能オブジェクトでなければなりません。ここ
   で "loop" はアクティブなイベントループへの参照であり、 "context" は
   例外の詳細な記述からなる "dict" オブジェクトです (*context* につい
   ての詳細は "call_exception_handler()" メソッドのドキュメントを参照
   してください)。

   "Task" または "Handle" のためにハンドラーを呼び出す場合、そのタスク
   またはコールバックハンドルの "contextvars.Context" で実行します。

   バージョン 3.12 で変更: 例外が発生したタスクまたはハンドルの
   "Context" で、ハンドラーを呼び出す場合があるようになりました。

loop.get_exception_handler()

   現在の例外ハンドラを返します。カスタム例外ハンドラが設定されていな
   い場合は "None" を返します。

   Added in version 3.5.2.

loop.default_exception_handler(context)

   デフォルトの例外ハンドラーです。

   デフォルト例外ハンドラは、例外ハンドラが未設定の場合、例外が発生し
   た時に呼び出されます。デフォルト例外ハンドラの挙動を受け入れるため
   に、カスタム例外ハンドラから呼び出すことも可能です。

   引数 *context* の意味は "call_exception_handler()" と同じです。

loop.call_exception_handler(context)

   現在のイベントループ例外ハンドラーを呼び出します。

   *context* は以下のキーを含む "dict" オブジェクトです (将来の Python
   バージョンで新しいキーが追加される可能性があります):

   * 'message': エラーメッセージ;

   * 'exception' (任意): 例外オブジェクト;

   * 'future' (任意): "asyncio.Future" インスタンス;

   * 'task' (省略可能): "asyncio.Task" のインスタンス

   * 'handle' (任意): "asyncio.Handle" インスタンス;

   * 'protocol' (任意): プロトコル インスタンス;

   * 'transport' (任意): トランスポート インスタンス;

   * 'socket' (省略可能): "socket.socket" のインスタンス

   * 'source_traceback' (省略可能): 生成元のトレースバック

   * 'handle_traceback' (省略可能): ハンドルのトレースバック

   * 'asyncgen' (optional): Asynchronous generator that caused the
     exception.

   注釈:

     イベントループのサブクラスで、このメソッドをオーバーライドしない
     でください。 例外処理をカスタマイズするには
     "set_exception_handler()" を使ってください。


デバッグモードの有効化
----------------------

loop.get_debug()

   イベントループのデバッグモード ("bool") を取得します。

   環境変数 "PYTHONASYNCIODEBUG" に空でない文字列が設定されている場合
   のデフォルト値は "True"、そうでない場合は "False" になります。

loop.set_debug(enabled: bool)

   イベントループのデバッグモードを設定します。

   バージョン 3.7 で変更: 新しい Python 開発モード を使ってデバッグモ
   ードを有効化することができるようになりました。

loop.slow_callback_duration

   この属性は、「遅い」とみなす実行時間の最小値を秒単位で設定するため
   に使います。 デバッグモードが有効な場合、「遅い」コールバックがログ
   に記録されます。

   デフォルト値は 100 ミリ秒です。

参考: asyncio のデバッグモード。


サブプロセスの実行
------------------

この節で解説しているのは低水準のメソッドです。通常の async/await コー
ドでは、高水準の関数である "asyncio.create_subprocess_shell()" や
"asyncio.create_subprocess_exec()" を代わりに使うことを検討してくださ
い。

注釈:

  Windows では、デフォルトの "ProactorEventLoop" はサブプロセスをサポ
  ートしますが、"SelectorEventLoop" はサポートしません。 詳細は
  Windows でのサブプロセスのサポート を参照してください。

async loop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   *args* で指定されたひとつの、または複数の文字列引数からサブプロセス
   を生成します。

   *args* は下記のいずれかに当てはまる文字列のリストでなければなりませ
   ん:

   * "str";

   * または ファイルシステムのエンコーディング にエンコードされた
     "bytes"

   引数の最初の文字列はプログラムの実行ファイルを指定します。それに続
   く残りの文字列は引数を指定し、そのプログラムに対する "argv" を構成
   します。

   このメソッドは標準ライブラリの "subprocess.Popen" クラスを、
   "shell=False" かつ最初の引数に文字列のリストを渡して呼び出した場合
   に似ています。しかし、 "Popen" クラスは文字列のリストを引数としてひ
   とつだけ取るのに対して、 *subprocess_exec* は複数の文字列引数をとる
   ことができます。

   *protocol_factory* は "asyncio.SubprocessProtocol" クラスの派生クラ
   スを返す呼び出し可能オブジェクトでなければなりません。

   その他の引数:

   * *stdin* は下記のいずれかをとることができます:

     * ファイルライクオブジェクト

     * 既存のファイル記述子 (正の整数)。 例えば "os.pipe()" で作成した
       もの。

     * デフォルト値は "subprocess.PIPE" 定数で、この場合新規にパイプを
       生成して接続します。

     * "None" が設定された場合、サブプロセスは元のプロセスのファイルデ
       スクリプタを引き継ぎます。

     * "subprocess.DEVNULL" 定数を設定すると、特別なファイル
       "os.devnull" を使います。

   * *stdout* は下記のいずれかをとることができます:

     * ファイルライクオブジェクト

     * デフォルト値は "subprocess.PIPE" 定数で、この場合新規にパイプを
       生成して接続します。

     * "None" が設定された場合、サブプロセスは元のプロセスのファイルデ
       スクリプタを引き継ぎます。

     * "subprocess.DEVNULL" 定数を設定すると、特別なファイル
       "os.devnull" を使います。

   * *stderr* は下記のいずれかをとることができます:

     * ファイルライクオブジェクト

     * デフォルト値は "subprocess.PIPE" 定数で、この場合新規にパイプを
       生成して接続します。

     * "None" が設定された場合、サブプロセスは元のプロセスのファイルデ
       スクリプタを引き継ぎます。

     * "subprocess.DEVNULL" 定数を設定すると、特別なファイル
       "os.devnull" を使います。

     * "subprocess.STDOUT" 定数を設定すると、標準エラー出力ストリーム
       をプロセスの標準出力ストリームに接続します。

   * その他のすべてのキーワード引数は解釈されずにそのまま
     "subprocess.Popen" に渡されます。ただし、 *bufsize*、
     *universal_newlines*、 *shell*、 *text*、 *encoding* および
     *errors* は指定してはいけません。

     "asyncio" のサブプロセス API はストリームからテキストへのデコード
     をサポートしていません。ストリームからテキストに変換するには
     "bytes.decode()" 関数を使ってください。

   *stdin*、*stdout*、*stderr* として渡したファイルライクオブジェクト
   がパイプを表す場合、イベントループで使うには、パイプのもう一方の端
   を "connect_write_pipe()" または "connect_read_pipe()" で登録してく
   ださい。

   他の引数についての詳細は "subprocess.Popen" クラスのコンストラクタ
   を参照してください。

   "(transport, protocol)" のペアを返します。ここで *transport* は
   "asyncio.SubprocessTransport" 基底クラスに適合するオブジェクトで、
   *protocol* は *protocol_factory* によりインスタンス化されたオブジェ
   クトです。

   トランスポートを閉じた場合や、トランスポートがガベージコレクション
   で回収された場合、子プロセスがまだ実行中なら強制終了します。

async loop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   コマンド *cmd* からプラットフォームの "シェル" シンタックスを使って
   サブプロセスを生成します。 *cmd* は "str" 文字列もしくは ファイルシ
   ステムのエンコーディング でエンコードされた "bytes" 文字列です。

   これは標準ライブラリの "subprocess.Popen" クラスを "shell=True" で
   呼び出した場合と似ています。

   *protocol_factory* は "SubprocessProtocol" の派生クラスを返す呼び出
   し可能オブジェクトでなければなりません。

   その他の引数についての詳細は "subprocess_exec()" メソッドを参照して
   ください。

   "(transport, protocol)" のペアを返します。ここで *transport* は
   "SubprocessTransport" 基底クラスに適合するオブジェクトで、
   *protocol* は *protocol_factory* によりインスタンス化されたオブジェ
   クトです。

   トランスポートを閉じた場合や、トランスポートがガベージコレクション
   で回収された場合、子プロセスがまだ実行中なら強制終了します。

注釈:

  シェルインジェクション の脆弱性を回避するために全ての空白文字および
  特殊文字を適切にクオートすることは、アプリケーション側の責任で確実に
  行ってください。シェルコマンドを構成する文字列内の空白文字と特殊文字
  のエスケープは、 "shlex.quote()" 関数を使うと適切に行うことができま
  す。


コールバックハンドル
====================

class asyncio.Handle

   "loop.call_soon()" や "loop.call_soon_threadsafe()" が返すコールバ
   ックのラッパーです。

   get_context()

      ハンドルに関連付けられた "contextvars.Context" オブジェクトを返
      します。

      Added in version 3.12.

   cancel()

      コールバックをキャンセルします。コールバックがキャンセル済みまた
      は実行済みの場合、このメソッドは何の影響もありません。

   cancelled()

      コールバックがキャンセルされた場合 "True" を返します。

      Added in version 3.7.

class asyncio.TimerHandle

   A callback wrapper object returned by "loop.call_later()", and
   "loop.call_at()".

   このクラスは "Handle" の派生クラスです。

   when()

      コールバックのスケジュール時刻を秒単位の "float" で返します。

      戻り値の時刻は絶対値で、 "loop.time()" と同じ参照時刻を使って定
      義されています。

      Added in version 3.7.


サーバーオブジェクト
====================

Server オブジェクトは "loop.create_server()"、
"loop.create_unix_server()"、 "start_server()" および
"start_unix_server()" 関数により生成されます。

"Server" クラスを直接インスタンス化しないでください。

class asyncio.Server

   *Server* オブジェクトは非同期のコンテキストマネージャです。 "async
   with" 文の中で使われた場合、 "async with" 文が完了した時に Server
   オブジェクトがクローズされること、およびそれ以降に接続を受け付けな
   いことが保証されます。

      srv = await loop.create_server(...)

      async with srv:
          # 何らかの処理

      # この時点で srv は閉じられており、新しい接続を受け付けない。

   バージョン 3.7 で変更: Python 3.7 から、 Server オブジェクトは非同
   期のコンテキストマネージャになりました。

   バージョン 3.11 で変更: このクラスは Python 3.9.11、3.10.3、3.11 で
   "asyncio.Server" として公開されました。

   close()

      サーバーを停止します: 待機しているソケットをクローズし "sockets"
      属性に "None" を設定します。

      既存の受信中のクライアントとの接続を表すソケットはオープンのまま
      です。

      サーバーは非同期に閉じられます。 サーバーが閉じられ、すべての接
      続が終了するまで待つには、"wait_closed()" コルーチンを使ってくだ
      さい。

   close_clients()

      受け付け済みのすべてのクライアント接続を閉じます。

      関連付けられたすべてのトランスポートに対して "close()" を呼び出
      します。

      新しいクライアント接続との競合を避けるため、サーバーを閉じる際は
      "close_clients()" の前に "close()" を呼び出してください。

      Added in version 3.13.

   abort_clients()

      保留中の処理の完了を待たずに、受け付け済みのすべてのクライアント
      接続を即座に閉じます。

      関連付けられたすべてのトランスポートに対して "abort()" を呼び出
      します。

      新しいクライアント接続との競合を避けるため、サーバーを閉じる際は
      "abort_clients()" の前に "close()" を呼び出してください。

      Added in version 3.13.

   get_loop()

      サーバオブジェクトに付随するイベントループを返します。

      Added in version 3.7.

   async start_serving()

      接続の受け付けを開始します。

      このメソッドはべき等なので、サーバーがすでに接続を受け付けていて
      も呼び出せます。

      キーワード専用のパラメータ *start_serving* を
      "loop.create_server()" や "asyncio.start_server()" メソッドに対
      して使用することにより、初期に接続を受け付けない Server オブジェ
      クトを生成することができます。この場合 "Server.start_serving()"
      または "Server.serve_forever()" メソッドを使ってオブジェクトが接
      続の受け付けを開始するようにすることができます。

      Added in version 3.7.

   async serve_forever()

      接続の受け入れを開始し、コルーチンがキャンセルされるまで継続しま
      す。 "serve_forever" タスクのキャンセルによりサーバーもクローズ
      されます。

      このメソッドはサーバーがすでに接続の受け入れを開始していても呼び
      出し可能です。ひとつの *Server* オブジェクトにつき
      "serve_forever" タスクはひとつだけ存在できます。

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

         async def client_connected(reader, writer):
             # reader/writer ストリームでクライアントと通信する。
             # 例えば次のようにする:
             await reader.readline()

         async def main(host, port):
             srv = await asyncio.start_server(
                 client_connected, host, port)
             await srv.serve_forever()

         asyncio.run(main('127.0.0.1', 0))

      Added in version 3.7.

   is_serving()

      サーバーが新規に接続の受け入れを開始した場合 "True" を返します。

      Added in version 3.7.

   async wait_closed()

      "close()" メソッドが完了し、すべてのアクティブな接続が終了するま
      で待ちます。

      バージョン 3.12 で変更: "wait_closed()" は、サーバーが閉じられ、
      すべてのアクティブな接続が終了するまで待つようになりました。 以
      前は、サーバーがすでに閉じられていると、接続がまだ残っていても即
      座に返っていました。

   sockets

      List of socket-like objects, "asyncio.trsock.TransportSocket",
      which the server is listening on.

      バージョン 3.7 で変更: Python 3.7 より前のバージョンでは、
      "Server.sockets" は内部に持っているサーバーソケットのリストを直
      接返していました。 Python 3.7 ではリストのコピーが返されるように
      なりました。


イベントループの実装
====================

asyncio は2つの異なるイベントループの実装、 "SelectorEventLoop" と
"ProactorEventLoop"、 を提供します:

デフォルトでは、asyncio は "EventLoop" を使うように設定されています。

class asyncio.SelectorEventLoop

   "selectors" モジュールに基づく "AbstractEventLoop" のサブクラスです
   。

   プラットフォーム上で利用可能な最も効率の良い *selector* を使います
   。特定のセレクタ実装を使うように手動で構成することも可能です:

      import asyncio
      import selectors

      async def main():
         ...

      loop_factory = lambda: asyncio.SelectorEventLoop(selectors.SelectSelector())
      asyncio.run(main(), loop_factory=loop_factory)

   利用可能な環境: Unix, Windows.

class asyncio.ProactorEventLoop

   I/O 完了ポート (IOCP) を使う、Windows 用の "AbstractEventLoop" のサ
   ブクラスです。

   利用可能な環境: Windows.

   参考: I/O 完了ポートの MSDN ドキュメント。

class asyncio.EventLoop

      そのプラットフォームで利用可能な、最も効率的な
      "AbstractEventLoop" のサブクラスの別名です。

      Unix では "SelectorEventLoop"、Windows では "ProactorEventLoop"
      の別名です。

   Added in version 3.13.

class asyncio.AbstractEventLoop

   asyncio に適合するイベントループの抽象基底クラスです。

   イベントループのメソッド の節には、"AbstractEventLoop" の代替実装で
   定義すべきメソッドをすべて列挙しています。


使用例
======

この節の全ての使用例は **意図的に**  "loop.run_forever()" や
"loop.call_soon()" のような 低水準のイベントループ API の使用法を示し
ています。一方で現代的な asyncio アプリケーションはここに示すような方
法をほとんど必要としません。 "asyncio.run()" のような高水準の関数の使
用を検討してください。


call_soon() を使った Hello World
--------------------------------

"loop.call_soon()" メソッドを使ってコールバックをスケジュールする例で
す。コールバックは ""Hello World"" を出力しイベントループを停止します:

   import asyncio

   def hello_world(loop):
       """'Hello World' を表示してイベントループを停止するコールバック"""
       print('Hello World')
       loop.stop()

   loop = asyncio.new_event_loop()

   # hello_world() の呼び出しをスケジュールする
   loop.call_soon(hello_world, loop)

   # loop.stop() で終了するブロッキング呼び出し
   try:
       loop.run_forever()
   finally:
       loop.close()

参考: コルーチンと "run()" 関数を使用した同じような Hello World の例。


call_later() で現在の日時を表示する
-----------------------------------

毎秒現在時刻を表示するコールバックの例です。コールバックは
"loop.call_later()" メソッドを使って自身を5秒後に実行するよう再スケジ
ュールし、イベントループを停止します:

   import asyncio
   import datetime as dt

   def display_date(end_time, loop):
       print(dt.datetime.now())
       if (loop.time() + 1.0) < end_time:
           loop.call_later(1, display_date, end_time, loop)
       else:
           loop.stop()

   loop = asyncio.new_event_loop()

   # display_date() の最初の呼び出しをスケジュールする
   end_time = loop.time() + 5.0
   loop.call_soon(display_date, end_time, loop)

   # loop.stop() で終了するブロッキング呼び出し
   try:
       loop.run_forever()
   finally:
       loop.close()

参考: コルーチンと "run()" 関数を使用した同じような 現在時刻出力 の例。


読み込みイベント用ファイル記述子の監視
--------------------------------------

ファイル記述子が "loop.add_reader()" メソッドを使って何らかのデータを
受信するまで待機し、その後イベントループをクローズします:

   import asyncio
   from socket import socketpair

   # 接続されたファイル記述子のペアを作成する
   rsock, wsock = socketpair()

   loop = asyncio.new_event_loop()

   def reader():
       data = rsock.recv(100)
       print("Received:", data.decode())

       # 処理が済んだので、ファイル記述子の登録を解除する
       loop.remove_reader(rsock)

       # イベントループを停止する
       loop.stop()

   # 読み込みイベント用にファイル記述子を登録する
   loop.add_reader(rsock, reader)

   # ネットワークからのデータ受信を模倣する
   loop.call_soon(wsock.send, 'abc'.encode())

   try:
       # イベントループを実行する
       loop.run_forever()
   finally:
       # 処理が済んだので、ソケットとイベントループを閉じる。
       rsock.close()
       wsock.close()
       loop.close()

参考:

  * トランスポート、プロトコル、および "loop.create_connection()" メソ
    ッドを使用した同じような 例。

  * 高水準の "asyncio.open_connection()" 関数とストリームを使用したも
    うひとつの 実装例。


SIGINT および SIGTERM 用のシグナルハンドラーの設定
--------------------------------------------------

(この "signal" の例は Unix でのみ動作します。)

"loop.add_signal_handler()" メソッドで、シグナル "SIGINT" と "SIGTERM"
のハンドラーを登録します。

   import asyncio
   import functools
   import os
   import signal

   def ask_exit(signame, loop):
       print("got signal %s: exit" % signame)
       loop.stop()

   async def main():
       loop = asyncio.get_running_loop()

       for signame in {'SIGINT', 'SIGTERM'}:
           loop.add_signal_handler(
               getattr(signal, signame),
               functools.partial(ask_exit, signame, loop))

       await asyncio.sleep(3600)

   print("Event loop running for 1 hour, press Ctrl+C to interrupt.")
   print(f"pid {os.getpid()}: send SIGINT or SIGTERM to exit.")

   asyncio.run(main())
