こんにちは。
野中やすおです。
タイトルにある通り、PythonのsubprocessモジュールのPopen(ぴーおーぷん、と読むらしいです)クラスがreturncodeがゼロ以外の場合に例外を発生させるエラーハンドリング処理を実装しようとしていたのですが、subprocess.Popenがどういったものか分からず、少し実装に時間がかかってしまったのでメモを残そうと思います。
subprocess.Popenとは?
subprocessモジュールあるいは、Popenクラスの詳細な話はまた別記事でしたいと思います。
公式HPは以下のリンクになります。
https://docs.python.org/ja/3/library/subprocess.html#popen-objects
Popen.returncodeが0にならなかった理由
Popenはサブプロセスが実行されている間は、Popen オブジェクトのreturncode属性はNoneを返します。以下のコード例を使って紹介したいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from subprocess import run, Popen p = Popen(["ls", "-la"], shell=True) print("Popenオブジェクト", p) # Popenオブジェクト <subprocess.Popen object at 0x154021a35520> print("process id = %s" % p.pid) # process id = 13 print(p.returncode) # None print("wait実行前", run(["ps","uxf"], shell=True)) # PID TTY TIME CMD # 12 ? 00:00:00 python3 # 13 ? 00:00:00 sh <defunct> # 14 ? 00:00:00 sh # 16 ? 00:00:00 ps Popen.wait(p) print("wait実行後", run(["ps","uxf"], shell=True)) # PID TTY TIME CMD # 12 ? 00:00:00 python3 # 13 ? 00:00:00 sh <defunct> # 16 ? 00:00:00 ps print(p.returncode) # 0 |
まずは、subprocess.Popenで「ls -la」(ディレクトリ中にあるファイル詳細を全て表示させるコマンド) を実行します。
その返り値をpとし、単純にprintするとPopenオブジェクトが返ってきていることがわかります。Popenオブジェクトの説明がある公式HPは以下のURLから飛ぶことができますので、同時に参照お願いします。
https://docs.python.org/ja/3/library/subprocess.html#popen-objects
次に「”process id = %s” % p.pid」でPopenで実行しているプロセスidを確認します。例えばプロセスIDが14とします。次にp.returncodeをprintしてもNoneになります。つまりそのサブプロセスが終了していないことを示します。
なので、wait関数である「Popen.wait」を使って(その他poll関数などがあります)をサブプロセスが終わるまで待ちます。Popen.waitは、returncode属性を設定して返却します。ちなみにPopen.waitはPopen.wait(timeout=10)のように、引数としてtimeoutを設定することができ、プロセスが timeoutに設定した秒後に終了してない場合、TimeoutExpired 例外を発出します。TimeoutExpiredを使ったエラーハンドリングもできますね。
確認のために、Popen.waitの前後にrunを使って起動しているプロセスを確認します。そうするとwait実行前には、プロセスID 14のサブプロセスが実行されていること、wait実行後にはそのサブプロセスが終了し、printの結果から消えていることが確認できます。
最後にもう一度p.returncodeをprintするとコマンドの正常終了を表す0が返ってきています。
今までおざなりになっていたsubprocessの使い方が実装にハマったことで理解が深まりました。
そして例えば以下のような形でPopenで実行したコマンドがうまくいかない場合には以下のようにエラーハンドリングをして、処理を中断させることができます。
1 2 3 4 5 6 7 |
from subprocess import run, Popen p = Popen(["ls", "-la"], shell=True) if p.returncode !=0: raise RuntimeError else: return 'コマンド成功!' |
もしこの記事が参考になった方がいらっしゃれば幸いです!