18.5.6. サブプロセス

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 が指定された場合、サブプロセスの標準エラー出力ストリームは標準出力ストリームと同じパイプに接続されます。
  • その他のキーワード引数は、指定してはならない bufsizeuniversal_newlines および shell を除き、すべて解釈されずに subprocess.Popen に渡されます。

(transport, protocol) のペアを返します。transportBaseSubprocessTransport のインスタンスです。

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

引数については 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) のペアを返します。transportBaseSubprocessTransport のインスタンスです。

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

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

18.5.6.4. 定数

asyncio.subprocess.PIPE

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

asyncio.subprocess.STDOUT

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

asyncio.subprocess.DEVNULL

create_subprocess_shell() および create_subprocess_exec() の引数 stdinstdout あるいは 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 では、SIGTERMterminate() の別名になります。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 クラスはスレッド安全ではありません。

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()