サブプロセス¶
ソースコード: 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)¶
プロセスとのやりとりを行います:
stdin にデータを送信します (input が
Noneでない場合);stdin を閉じます;
EOF に達するまで stdout および stderr からデータを読み出します;
プロセスが終了するまで待ち受けます。
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であることは、プロセスがまだ終了していないことを示しています。create_subprocess_exec()で作成したプロセスでは、負の値-Nは子プロセスがシグナルNによって終了したことを示します (POSIX のみ)。create_subprocess_shell()で作成したプロセスでは、戻りコードはシェル自体 (例えば/bin/sh) の終了ステータスを反映します。 シェルによってはシグナルを128+Nなどのコードに対応付けます。 詳しくは、シェルのドキュメント (例えば Bash マニュアルの Exit Status) を参照してください。
サブプロセスとスレッド¶
標準的な asyncio のイベントループは、異なるスレッドからサブプロセスを実行するのをデフォルトでサポートしています。
Windows のサブプロセスは ProactorEventLoop (デフォルト) のみ提供され、 SelectorEventLoop はサブプロセスをサポートしていません。
標準で提供されない別のイベントループ実装の場合、固有の制限がある可能性があります; それぞれの実装のドキュメントを参照してください。
使用例¶
サブプロセスの制御のために Process クラスを使い、サブプロセスの標準出力を読み出すために StreamReader を使う例です。
サブプロセスは create_subprocess_exec() 関数により生成されます:
import asyncio
import sys
async def get_date():
code = 'import datetime as dt; print(dt.datetime.now())'
# サブプロセスを作成し、標準出力をパイプにリダイレクトする。
proc = await asyncio.create_subprocess_exec(
sys.executable, '-c', code,
stdout=asyncio.subprocess.PIPE)
# 出力を 1 行読み込む。
data = await proc.stdout.readline()
line = data.decode('ascii').rstrip()
# サブプロセスの終了を待つ。
await proc.wait()
return line
date = asyncio.run(get_date())
print(f"Current date: {date}")
低水準の API を使って書かれた 同様の例 も参照してください。