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

**ソースコード:** 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=None, **kwds)

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

   The *limit* argument sets the buffer limit for "StreamReader"
   wrappers for "stdout" and "stderr" (if "subprocess.PIPE" is passed
   to *stdout* and *stderr* arguments).

   "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=None, **kwds)

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

   The *limit* argument sets the buffer limit for "StreamReader"
   wrappers for "stdout" and "stderr" (if "subprocess.PIPE" is passed
   to *stdout* and *stderr* arguments).

   "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* に渡すことができます。

   If *PIPE* is passed to *stdin* argument, the "Process.stdin"
   attribute will point to a "StreamWriter" instance.

   If *PIPE* is passed to *stdout* or *stderr* arguments, the
   "Process.stdout" and "Process.stderr" attributes will point to
   "StreamReader" instances.

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

   An object that wraps OS processes created by the
   "create_subprocess_exec()" and "create_subprocess_shell()"
   functions.

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

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

   * the "communicate()" and "wait()" methods don't have a *timeout*
     parameter: use the "wait_for()" function;

   * "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* nに書き込んでいる時に
      "BrokenPipeError" または "ConnectionResetError" 例外が送出された
      場合、例外は無視されます。このような条件は、全てのデータが
      *stdin* に書き込まれる前にプロセスが終了した場合に起こります。

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

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

      バージョン 3.12 で変更: *stdin* gets closed when "input=None"
      too.

   send_signal(signal)

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

      注釈:

        On Windows, "SIGTERM" is an alias for "terminate()".
        "CTRL_C_EVENT" and "CTRL_BREAK_EVENT" can be sent to processes
        started with a *creationflags* parameter which includes
        "CREATE_NEW_PROCESS_GROUP".

   terminate()

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

      On POSIX systems this method sends "SIGTERM" to the child
      process.

      On Windows the Win32 API function "TerminateProcess()" is called
      to stop the child process.

   kill()

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

      On POSIX systems this method sends "SIGKILL" to the child
      process.

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

   stdin

      Standard input stream ("StreamWriter") or "None" if the process
      was created with "stdin=None".

   stdout

      Standard output stream ("StreamReader") or "None" if the process
      was created with "stdout=None".

   stderr

      Standard error stream ("StreamReader") or "None" if the process
      was created with "stderr=None".

   警告:

     Use the "communicate()" method rather than
     "process.stdin.write()", "await process.stdout.read()" or "await
     process.stderr.read()". This avoids deadlocks due to streams
     pausing reading or writing and blocking the child process.

   pid

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

      Note that for processes created by the
      "create_subprocess_shell()" function, this attribute is the PID
      of the spawned shell.

   returncode

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

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

      負の値 "-N" は子プロセスがシグナル "N" により中止させられたこと
      を示します (POSIX のみ)。


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

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

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

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

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


使用例
------

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

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

   import asyncio
   import sys

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

       # Create the subprocess; redirect the standard output
       # into a pipe.
       proc = await asyncio.create_subprocess_exec(
           sys.executable, '-c', code,
           stdout=asyncio.subprocess.PIPE)

       # Read one line of output.
       data = await proc.stdout.readline()
       line = data.decode('ascii').rstrip()

       # Wait for the subprocess exit.
       await proc.wait()
       return line

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

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