サブプロセス
************

**ソースコード:** Lib/asyncio/subprocess.py,
Lib/asyncio/base_subprocess.py

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

このセクションはサブプロセスの生成と管理を行う高水準の async/await
asyncio API について説明します。

以下は、 asyncio モジュールがどのようにシェルコマンドを実行し、結果を
取得できるかを示す例です:

   import asyncio

   async def run(cmd):
       proc = await asyncio.create_subprocess_shell(
           cmd,
           stdout=asyncio.subprocess.PIPE,
           stderr=asyncio.subprocess.PIPE)

       stdout, stderr = await proc.communicate()

       print(f'[{cmd!r} exited with {proc.returncode}]')
       if stdout:
           print(f'[stdout]\n{stdout.decode()}')
       if stderr:
           print(f'[stderr]\n{stderr.decode()}')

   asyncio.run(run('ls /zzz'))

このサンプルコードは以下を出力します:

   ['ls /zzz' exited with 1]
   [stderr]
   ls: /zzz: No such file or directory

asyncio のサブプロセス関数はすべて非同期で、asyncio にはこれらの関数を
扱うためのツールが多数あるため、複数のサブプロセスを並列に実行して監視
するのは簡単です。 実際、上の例を変更して複数のコマンドを同時に実行す
るのはごく簡単です。

   async def main():
       await asyncio.gather(
           run('ls /zzz'),
           run('sleep 1; echo "hello"'))

   asyncio.run(main())

使用例 節も参照してください。


サブプロセスの生成
==================

async asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=65536, **kwds)

   サブプロセスを作成します。

   *limit* 引数は、"stdout" と "stderr" をラップする "StreamReader" の
   バッファの上限を設定します (*stdout* と *stderr* 引数に
   "subprocess.PIPE" を渡した場合)。

   "Process" のインスタンスを返します。

   他のパラメータについては "loop.subprocess_exec()" を参照してくださ
   い。

   If the process object is garbage collected while the process is
   still running, the child process will be killed.

   バージョン 3.10 で変更: *loop* パラメータが削除されました。

async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=65536, **kwds)

   シェルコマンド *cmd* を実行します。

   *limit* 引数は、"stdout" と "stderr" をラップする "StreamReader" の
   バッファの上限を設定します (*stdout* と *stderr* 引数に
   "subprocess.PIPE" を渡した場合)。

   "Process" のインスタンスを返します。

   他のパラメータについては "loop.subprocess_shell()" のドキュメントを
   参照してください。

   If the process object is garbage collected while the process is
   still running, the child process will be killed.

   重要:

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

   バージョン 3.10 で変更: *loop* パラメータが削除されました。

注釈:

  サブプロセスは、 "ProactorEventLoop" を使えば Windows でも利用可能で
  す。詳しくは Windows におけるサブプロセスのサポート を参照してくださ
  い。

参考:

  asyncio は以下に挙げるサブプロセスと協調するための *低水準の* API も
  持っています: "loop.subprocess_exec()", "loop.subprocess_shell()",
  "loop.connect_read_pipe()", "loop.connect_write_pipe()" および
  Subprocess Transports と Subprocess Protocols。


定数
====

asyncio.subprocess.PIPE

   *stdin*, *stdout* または *stderr* に渡すことができます。

   *stdin* 引数に *PIPE* を渡すと、"Process.stdin" 属性は
   "StreamWriter" インスタンスを指します。

   *stdout* または *stderr* 引数に *PIPE* を渡すと、対応する
   "Process.stdout" 属性または "Process.stderr" 属性は "StreamReader"
   インスタンスを指します。

asyncio.subprocess.STDOUT

   *stderr* 引数に対して利用できる特殊な値で、標準エラー出力が標準出力
   にリダイレクトされることを意味します。

asyncio.subprocess.DEVNULL

   プロセスを生成する関数の *stdin*, *stdout* または *stderr* 引数に利
   用できる特殊な値です。対応するサブプロセスのストリームに特殊なファ
   イル "os.devnull" が使われることを意味します。


サブプロセスとやりとりする
==========================

"create_subprocess_exec()" と "create_subprocess_shell()" の2つの関数
はどちらも *Process* クラスのインスタンスを返します。 *Process* クラス
はサブプロセスと通信したり、サブプロセスの完了を監視したりするための高
水準のラッパーです。

class asyncio.subprocess.Process

   "create_subprocess_exec()" 関数や "create_subprocess_shell()" 関数
   で作成した OS のプロセスをラップするオブジェクトです。

   このクラスは "subprocess.Popen" クラスと同様の API を持つように設計
   されていますが、いくつかの注意すべき違いがあります:

   * Popen と異なり、 Process インスタンスは "poll()" メソッドに相当す
     るメソッドを持っていません;

   * "communicate()" メソッドと "wait()" メソッドには *timeout* パラメ
     ーターがありません。 "wait_for()" 関数を使ってください。

   * "subprocess.Popen.wait()" メソッドが同期処理のビジーループとして
     実装されているのに対して、 "Process.wait()" メソッドは非同期処理
     です;

   * *universal_newlines* パラメータはサポートされていません。

   このクラスは スレッド安全ではありません。

   サブプロセスとスレッド 節も参照してください。

   async wait()

      子プロセスが終了するのを待ち受けます。

      "returncode" 属性を設定し、その値を返します。

      注釈:

        "stdout=PIPE" または "stderr=PIPE" を使う場合、子プロセスが大
        量に出力して OS のパイプバッファに空きができるまでブロックされ
        ると、このメソッドはデッドロックする可能性があります。 パイプ
        を使うときは "communicate()" メソッドでこの状況を回避してくだ
        さい。

   async communicate(input=None)

      プロセスとのやりとりを行います:

      1. *stdin* にデータを送信します (*input* が "None" でない場合);

      2. *stdin* を閉じます;

      3. EOF に達するまで *stdout* および *stderr* からデータを読み出
         します;

      4. プロセスが終了するまで待ち受けます。

      *input* オプション引数は子プロセスに送信されるデータ ("bytes" オ
      ブジェクト) です。

      "(stdout_data, stderr_data)" のタプルを返します。

      *input* を *stdin* に書き込む際に "BrokenPipeError" または
      "ConnectionResetError" が送出された場合、その例外は無視されます
      。 これは、すべてのデータを *stdin* に書き込む前にプロセスが終了
      すると発生します。

      子プロセスの標準入力 *stdin* にデータを送りたい場合、プロセスは
      "stdin=PIPE" を設定して生成する必要があります。同様に、 "None"
      以外の何らかのデータを戻り値のタプルで受け取りたい場合、プロセス
      は "stdout=PIPE" と "stderr=PIPE" のいずれかまたは両方を指定して
      生成しなければなりません。

      プロセスから受信したデータはメモリ上にバッファーされることに注意
      してください。そのため、返されるデータのサイズが大きいかまたは無
      制限の場合はこのメソッドを使わないようにしてください。

      バージョン 3.12 で変更: "input=None" の場合も *stdin* を閉じるよ
      うになりました。

   send_signal(signal)

      子プロセスにシグナル *signal* を送信します。

      注釈:

        Windows では "SIGTERM" の送信は "terminate()" の呼び出しと同じ
        です。 *creationflags* パラメーターに
        "CREATE_NEW_PROCESS_GROUP" を含めて起動したプロセスには、
        "CTRL_C_EVENT" と "CTRL_BREAK_EVENT" を送信できます。

   terminate()

      子プロセスを停止します。

      POSIX システムでは、子プロセスに "SIGTERM" を送信します。

      Windows では、Win32 API 関数 "TerminateProcess()" を呼び出して子
      プロセスを停止します。

   kill()

      子プロセスを強制終了 (kill) します。

      POSIX システムでは、子プロセスに "SIGKILL" を送信します。

      Windows では、このメソッドは "terminate()" のエイリアスです。

   stdin

      標準入力ストリーム ("StreamWriter") です。 "stdin=None" でプロセ
      スを作成した場合は "None" です。

   stdout

      標準出力ストリーム ("StreamReader") です。 "stdout=None" でプロ
      セスを作成した場合は "None" です。

   stderr

      標準エラーストリーム ("StreamReader") です。 "stderr=None" でプ
      ロセスを作成した場合は "None" です。

   警告:

     "process.stdin.write()"、"await process.stdout.read()"、"await
     process.stderr.read()" の代わりに "communicate()" メソッドを使っ
     てください。 これにより、ストリームの読み書きが一時停止して子プロ
     セスがブロックされることによるデッドロックを回避できます。

   pid

      子プロセスのプロセス番号 (PID) です。

      "create_subprocess_shell()" 関数で作成したプロセスでは、この属性
      は起動したシェルの PID です。

   returncode

      プロセスが終了した時の終了ステータスを返します。

      この属性が "None" であることは、プロセスがまだ終了していないこと
      を示しています。

      "create_subprocess_exec()" で作成したプロセスでは、負の値 "-N"
      は子プロセスがシグナル "N" によって終了したことを示します (POSIX
      のみ)。

      "create_subprocess_shell()" で作成したプロセスでは、戻りコードは
      シェル自体 (例えば "/bin/sh") の終了ステータスを反映します。 シ
      ェルによってはシグナルを "128+N" などのコードに対応付けます。 詳
      しくは、シェルのドキュメント (例えば Bash マニュアルの Exit
      Status) を参照してください。


サブプロセスとスレッド
----------------------

標準的な asyncio のイベントループは、異なるスレッドからサブプロセスを
実行するのをデフォルトでサポートしています。

Windows のサブプロセスは "ProactorEventLoop"  (デフォルト) のみ提供さ
れ、 "SelectorEventLoop" はサブプロセスをサポートしていません。

標準で提供されない別のイベントループ実装の場合、固有の制限がある可能性
があります; それぞれの実装のドキュメントを参照してください。

参考: 並行処理とマルチスレッド処理


使用例
------

サブプロセスの制御のために "Process" クラスを使い、サブプロセスの標準
出力を読み出すために "StreamReader" を使う例です。

サブプロセスは "create_subprocess_exec()" 関数により生成されます:

   import asyncio
   import sys

   async def get_date():
       code = 'import datetime as dt; print(dt.datetime.now())'

       # サブプロセスを作成し、標準出力をパイプにリダイレクトする。
       proc = await asyncio.create_subprocess_exec(
           sys.executable, '-c', code,
           stdout=asyncio.subprocess.PIPE)

       # 出力を 1 行読み込む。
       data = await proc.stdout.readline()
       line = data.decode('ascii').rstrip()

       # サブプロセスの終了を待つ。
       await proc.wait()
       return line

   date = asyncio.run(get_date())
   print(f"Current date: {date}")

低水準の API を使って書かれた 同様の例 も参照してください。
