サブプロセス

ソースコード: 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 モジュールはこれらの非同期関数と協調するための多くのツールを提供しているので、複数のサブプロセスを並列に実行して監視することは簡単です。実際、上記のサンプル小0どを複数のコマンドを同時に実行するように修正するのはきわめて単純です:

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

asyncio.run(main())

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

サブプロセスの生成

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

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

引数 limit は (subprocess.PIPEstdoutstderr に設定した場合の) Process.stdoutProcess.stderr のための StreamReader ラッパーのバッファー上限値を設定します。

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

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

Deprecated since version 3.8, will be removed in version 3.10: loop パラメータ。

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

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

引数 limit は (subprocess.PIPEstdoutstderr に設定した場合の) Process.stdoutProcess.stderr のための StreamReader ラッパーのバッファー上限値を設定します。

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

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

重要

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

Deprecated since version 3.8, will be removed in version 3.10: loop パラメータ。

注釈

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

参考

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

定数

asyncio.subprocess.PIPE

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

PIPEstdin 引数に渡された場合、 Process.stdin 属性は StreamWriter インスタンスを指します。

PIPEstdoutstderr 引数に渡された場合、 Process.stdoutProcess.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() メソッドに相当するメソッドを持っていません;

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

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

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

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

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

coroutine wait()

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

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

注釈

stdout=PIPE または stderr=PIPE を使っており、OS パイプバッファがさらなるデータを受け付けるようになるまで子プロセスをブロックするほど大量の出力を生成場合、このメソッドはデッドロックする可能性があります。この条件を避けるため、パイプを使用する場合は communicate() メソッドを使ってください。

coroutine communicate(input=None)

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

  1. stdin にデータを送信します (inputNone でない場合);

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

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

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

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

input を標準入力 stdin nに書き込んでいる時に BrokenPipeError または ConnectionResetError 例外が送出された場合、例外は無視されます。このような条件は、全てのデータが stdin に書き込まれる前にプロセスが終了した場合に起こります。

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

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

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 では、このメソッドは terminate() のエイリアスです。

stdin

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

stdout

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

stderr

標準エラー出力ストリーム (StreamReader) です。プロセスが stderr=None で生成された場合は 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) です。

create_subprocess_shell() 関数によって生成されたプロセスの場合、この属性は生成されたシェルの PID になることに注意してください。

returncode

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

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

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

サブプロセスとスレッド

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

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

UNIX の child watchers はサブプロセスの終了を待ち受けるために使われます。より詳しい情報については プロセスのウオッチャー を参照してください。

バージョン 3.8 で変更: UNIX では、異なるスレッドから何らの制限なくサブプロセスを生成するために ThreadedChildWatcher を使うようになりました。

現在の子プロセスのウオッチャーが アクティブでない 場合にサブプロセスを生成すると RuntimeError 例外が送出されます。

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

使用例

サブプロセスの制御のために 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 を使って書かれた 同様の例 も参照してください。