サブプロセス

ソースコード: 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 であることは、プロセスがまだ終了していないことを示しています。

For processes created with create_subprocess_exec(), a negative value -N indicates that the child was terminated by signal N (POSIX only).

For processes created with create_subprocess_shell(), the return code reflects the exit status of the shell itself (e.g. /bin/sh), which may map signals to codes such as 128+N. See the documentation of the shell (for example, the Bash manual's Exit Status) for details.

サブプロセスとスレッド

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

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

On UNIX child watchers are used for subprocess finish waiting, see Process Watchers for more info.

バージョン 3.8 で変更: UNIX switched to use ThreadedChildWatcher for spawning subprocesses from different threads without any limitation.

Spawning a subprocess with inactive current child watcher raises RuntimeError.

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

使用例

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

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

import asyncio
import sys

async def get_date():
    code = 'import datetime as dt; print(dt.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 を使って書かれた 同様の例 も参照してください。