18.5.6. サブプロセス
********************

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


18.5.6.1. Windows でのイベントループ
====================================

Windows では、デフォルトのイベントループは "SelectorEventLoop" になり
ますが、これはサブプロセスをサポートしていません。代わりに
"ProactorEventLoop" を使用します。Windows で使用する例:

   import asyncio, sys

   if sys.platform == 'win32':
       loop = asyncio.ProactorEventLoop()
       asyncio.set_event_loop(loop)

参考: 利用可能なイベントループ および プラットフォームでのサポート。


18.5.6.2. サブプロセスの作成: Process を使用した高水準 API
==========================================================

coroutine asyncio.create_subprocess_exec(*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

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

   *limit* 引数で "StreamReader" に渡すバッファリミットを設定します。
   他の引数については "AbstractEventLoop.subprocess_exec()" を参照して
   ください。

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

   この関数は コルーチン です。

coroutine asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, **kwds)

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

   *limit* 引数で "StreamReader" に渡すバッファリミットを設定します。
   他の引数については "AbstractEventLoop.subprocess_shell()" を参照し
   てください。

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

   シェルインジェクション の脆弱性を回避するために、すべての空白文字お
   よびメタ文字を適切にクオートすることはアプリケーション側の責任です
   。 "shlex.quote()" 関数は、シェルコマンドで使用される文字列内の、空
   白文字とシェルのメタ文字の適切なエスケープに使用できます。

   この関数は コルーチン です。

パイプに接続するには "AbstractEventLoop.connect_read_pipe()" および
"AbstractEventLoop.connect_write_pipe()" メソッドを使用します。


18.5.6.3. サブプロセスの作成: subprocess.Popen を使用した低水準 API
===================================================================

"subprocess" モジュールを使用して非同期にサブプロセスを実行します。

coroutine AbstractEventLoop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   1 個以上の文字列引数 (ファイルシステムエンコーディング にエンコード
   された文字列またはバイト列) からサブプロセスを作成します。先頭の文
   字列で実行するプログラムを指定し、残りの文字列でプログラムの引数を
   指定します (それが Python スクリプトであれば、"sys.argv" の値に相当
   します)。これは標準ライブラリの "subprocess.Popen" クラスが
   shell=False で呼び出され、文字列のリストが第 1 引数として渡されたと
   きと似ていますが、"Popen" が引数として文字列のリストを 1 個取るのに
   対し、"subprocess_exec()" 引数として複数の文字列を取ります。

   *protocol_factory* はクラス "asyncio.SubprocessProtocol" のサブクラ
   スを作成しなくてはなりません。

   その他の引数:

   * *stdin*: "connect_write_pipe()" を使用してサブプロセスの標準入
     力 ストリームに接続されたパイプを表すファイルライクオブジェクト、
     も しくは定数 "subprocess.PIPE" (デフォルト) のどちらかになります
     。 デフォルトでは、パイプが新しく作成され、接続されます。

   * *stdout*: "connect_read_pipe()", を使用してサブプロセスの標準出
     力 ストリームに接続されたパイプを表すファイルライクオブジェクト、
     も しくは定数 "subprocess.PIPE" (デフォルト) のどちらかになります
     。 デフォルトでは、パイプが新しく作成され、接続されます。

   * *stderr*: "connect_read_pipe()" を使用してサブプロセスの標準エ
     ラ ー出力ストリームに接続されたパイプを表すファイルライクオブジェ
     ク ト、もしくは定数 "subprocess.PIPE" (デフォルト) と定数
     "subprocess.STDOUT" のどちらかになります。 デフォルトでは、パイプ
     が新しく作成され、接続されます。 "subprocess.STDOUT" が指定された
     場合、サブプロセスの標準エラー出力ストリームは標準出力ストリーム
     と同じパイプに接続されます。

   * その他のキーワード引数は、指定してはならない *bufsize*、
     *universal_newlines* および *shell* を除き、すべて解釈されずに
     "subprocess.Popen" に渡されます。

   "(transport, protocol)" のペアを返します。*transport* は
   "BaseSubprocessTransport" のインスタンスです。

   このメソッドは コルーチン です。

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

coroutine AbstractEventLoop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   プラットフォームの "シェル" 構文を使用して *cmd* (ファイルシステム
   エンコーディング にエンコードされた文字列またはバイト列) からサブプ
   ロセスを作成します。これは標準ライブラリ "subprocess.Popen" クラス
   を "shell=True" で呼び出したときと似ています。

   *protocol_factory* はクラス "asyncio.SubprocessProtocol" のサブクラ
   スを作成しなくてはなりません。

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

   "(transport, protocol)" のペアを返します。*transport* は
   "BaseSubprocessTransport" のインスタンスです。

   シェルインジェクション の脆弱性を回避するために、すべての空白文字お
   よびメタ文字を適切にクオートすることはアプリケーション側の責任です
   。 "shlex.quote()" 関数は、シェルコマンドで使用される文字列内の、空
   白文字とシェルのメタ文字の適切なエスケープに使用できます。

   このメソッドは コルーチン です。

参考: "AbstractEventLoop.connect_read_pipe()" および
  "AbstractEventLoop.connect_write_pipe()" メソッド。


18.5.6.4. 定数
==============

asyncio.subprocess.PIPE

   "create_subprocess_shell()" および "create_subprocess_exec()" の引
   数 *stdin*、*stdout* あるいは *stderr* で使用できる特殊な値です。オ
   ープンすべき標準ストリームへのパイプを示します。

asyncio.subprocess.STDOUT

   "create_subprocess_shell()" および "create_subprocess_exec()" の引
   数 *stderr* で使用できる特殊な値です。標準エラー出力を標準出力と同
   様に扱うための標準出力へのパイプを示します。

asyncio.subprocess.DEVNULL

   "create_subprocess_shell()" および "create_subprocess_exec()" の引
   数 *stdin*、*stdout* あるいは *stderr* で使用できる特殊な値です。特
   殊ファイル "os.devnull" を使用するよう示します。


18.5.6.5. Process
=================

class asyncio.subprocess.Process

   関数 "create_subprocess_exec()" あるいは
   "create_subprocess_shell()" によって作成されたサブプロセスです。

   "Process" クラスの API は "subprocess.Popen" クラスの API に似せて
   設計されましたが、一部異なります:

   * 明示的な "poll()" メソッドはありません

   * "communicate()" および "wait()" メソッドは *timeout* 引数を取り
     ま せん: "wait_for()" 関数を使用してください

   * *universal_newlines* 引数はサポートされません (バイト文字列のみ
     サ ポートします)

   * "Process" クラスの "wait()" メソッドは非同期であるのに対し、
     "Popen" クラスの "wait()" メソッドはビジーループとして実装されて
     います。

   このクラスは スレッド安全ではありません。 asyncio-subprocess-
   threads 節を参照してください。

   coroutine wait()

      プロセスの終了を待ちます。リターンコードが "returncode" 属性に設
      定され、返されます。

      このメソッドは コルーチン です。

      注釈: "stdout=PIPE" または "stderr=PIPE" を使用しており、OS パ
        イプバ ッファのそれ以上のデータを受け取りの待機をブロックする
        ほど子プ ロセスがパイプに出力した場合、このメソッドはデッドロ
        ックします 。 これを回避するには "communicate()" メソッドを使
        用してくださ い。

   coroutine communicate(input=None)

      プロセスと交信、すなわち、標準入力へのデータ送信、EOF に達するま
      で標準出力および標準エラー出力からのデータ受信、およびプロセスの
      終了の待機を行います。任意の引数 *input* は子プロセスに送信する
      データを設定します。送信するデータがない場合は "None" を設定しま
      す (デフォルト)。*input* はバイト列でなければなりません。

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

      *input* を標準入力に書き込んだときに "BrokenPipeError" または
      "ConnectionResetError" 例外が送出された場合、例外は無視されます
      。これは全データが標準入力に書き込まれる前にプロセスが終了したと
      きに発生します。

      プロセスの標準入力にデータを送りたい場合、"stdin=PIPE" で
      Process オブジェクトを作成する必要があります。同様に、結果のタプ
      ルに "None" 以外のものを受け取りたい場合には "stdout=PIPE" およ
      び/または "stderr=PIPE" を指定する必要があります。

      このメソッドは コルーチン です。

      注釈: 受信したデータはメモリにバッファーされます。そのため、返
        される データが大きいかあるいは制限がないような場合はこのメソ
        ッドを使 うべきではありません。

      バージョン 3.4.2 で変更: このメソッドは "BrokenPipeError" および
      "ConnectionResetError" を無視するようになりました。

   send_signal(signal)

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

      注釈: Windows では、"SIGTERM" は "terminate()" の別名になりま
        す。 "CTRL_C_EVENT" および "CTRL_BREAK_EVENT" で、
        *creationflags* で始まり、"CREATE_NEW_PROCESS_GROUP" を含むパ
        ラメータをプロセ スに送信することができます。

   terminate()

      子プロセスを停止します。POSIX システムでは、子プロセスに
      "signal.SIGTERM" を送信します。Windows では、Win32 API 関数
      "TerminateProcess()" が呼び出されます。

   kill()

      子プロセスを kill します。POSIX システムでは "SIGKILL" を子プロ
      セスに送信します。Windows では "kill()" は "terminate()" の別名
      になります。

   stdin

      標準入力ストリーム ("StreamWriter") になります。プロセスが
      "stdin=None" で作成されていた場合 "None" になります。

   stdout

      標準出力ストリーム ("StreamReader") になります。プロセスが
      "stdout=None" で作成されていた場合 "None" になります。

   stderr

      標準エラー出力ストリーム ("StreamReader") になります。プロセスが
      "stderr=None" で作成されていた場合 "None" になります。

   警告: ストリームの読み込みまたは書き込みの一時停止およびプロセス
     のブロ ックによるデッドロックを回避するには、".stdin.write"、
     ".stdout.read" あるいは ".stderr.read" ではなく、"communicate()"
     メソッドを使用してください。

   pid

      プロセスの識別子です。

      "create_subprocess_shell()" 関数によって作成されたプロセスでは、
      この属性は生成したシェルのプロセス識別子になる点に注意してくださ
      い。

   returncode

      プロセスが終了したときのリターンコードです。"None" 値はプロセス
      がまだ終了していないことを示します。

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


18.5.6.6. サブプロセスとスレッド
================================

asyncio はサブプロセスを異なるスレッドから実行するのをサポートしていま
すが、制限があります:

* イベントループはメインスレッド内で実行されなければなりません

* 子ウォッチャーは、他のスレッドからサブプロセスが実行される前に、メ
  イ ンスレッドで作成されなければなりません。 メインスレッドで
  "get_child_watcher()" を呼んで子ウォッチャーをインスタンス化してくだ
  さい。

"asyncio.subprocess.Process" クラスはスレッド安全ではありません。

参考: asyncio-multithreading


18.5.6.7. サブプロセスの例
==========================


18.5.6.7.1. トランスポートおよびプロトコルを使用したサブプロセス
----------------------------------------------------------------

サブプロセスの出力を取得しサブプロセスの終了を待機するサブプロセスプロ
トコルの例です。サブプロセスは "AbstractEventLoop.subprocess_exec()"
メソッドで作成されます:

   import asyncio
   import sys

   class DateProtocol(asyncio.SubprocessProtocol):
       def __init__(self, exit_future):
           self.exit_future = exit_future
           self.output = bytearray()

       def pipe_data_received(self, fd, data):
           self.output.extend(data)

       def process_exited(self):
           self.exit_future.set_result(True)

   @asyncio.coroutine
   def get_date(loop):
       code = 'import datetime; print(datetime.datetime.now())'
       exit_future = asyncio.Future(loop=loop)

       # Create the subprocess controlled by the protocol DateProtocol,
       # redirect the standard output into a pipe
       create = loop.subprocess_exec(lambda: DateProtocol(exit_future),
                                     sys.executable, '-c', code,
                                     stdin=None, stderr=None)
       transport, protocol = yield from create

       # Wait for the subprocess exit using the process_exited() method
       # of the protocol
       yield from exit_future

       # Close the stdout pipe
       transport.close()

       # Read the output which was collected by the pipe_data_received()
       # method of the protocol
       data = bytes(protocol.output)
       return data.decode('ascii').rstrip()

   if sys.platform == "win32":
       loop = asyncio.ProactorEventLoop()
       asyncio.set_event_loop(loop)
   else:
       loop = asyncio.get_event_loop()

   date = loop.run_until_complete(get_date(loop))
   print("Current date: %s" % date)
   loop.close()


18.5.6.7.2. ストリームを使用したサブプロセス
--------------------------------------------

サブプロセスを制御する "Process" クラスと標準出力から読み込む
"StreamReader" クラスを使用した例で得す。サブプロセスは
"create_subprocess_exec()" 関数で作成されます:

   import asyncio.subprocess
   import sys

   @asyncio.coroutine
   def get_date():
       code = 'import datetime; print(datetime.datetime.now())'

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

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

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

   if sys.platform == "win32":
       loop = asyncio.ProactorEventLoop()
       asyncio.set_event_loop(loop)
   else:
       loop = asyncio.get_event_loop()

   date = loop.run_until_complete(get_date())
   print("Current date: %s" % date)
   loop.close()
