naiの日記

ソフトウェアエンジニアから放射線科診断医にジョブチェンジしました。趣味のことを書きます。

Chrome Native Messagingのホストからsubprocessを呼ぶ際の注意点

最近は趣味でChrome拡張機能の開発を行っていますが、Native MessagingのホストであるPythonプログラムから subprocess.check_call を実行すると、直後に実行中のTkinterウィンドウが閉じてプログラムが終了するという現象に見舞われました。最初は何らかのヤバイ例外が発生して親プロセスもろとも異常終了したのかと思いましたが、確認してみるとsubprocessから実行した処理は正常に終了しています。はて……?

原因はsubprocessの標準出力を捕捉していないことでした。 subprocess.run などのメソッドはデフォルトで stdout=None 、つまりリダイレクトを行わない設定となっており、生成されたプロセスは親プロセスと同じ入出力ストリームを使用します。ここで、Chrome拡張機能とNative Messagingホストは標準入出力を用いてメッセージのやり取りを行うため、子プロセスの出力もChrome側に送られてしまい、異常なメッセージと判断されコネクションが切断されるようです。

というわけで対処法としては、適切に標準入出力を捕捉してやればOKです。

cp = subprocess.run(
    ...,
    stdin=subprocess.DEVNULL,
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
)
self.log(cp.stdout.decode())

こんな感じ。完全に標準出力を捨てるなら stdout=subprocess.DEVNULL でよいですが、一応取得して self.log で出力するような感じにしました。また、使わないなら stdin も念の為 subprocess.DEVNULL にしておいた方が良いでしょう。

ちなみに親プロセスの Tkinter.Frame ウィンドウが閉じた理由は、単純に切断時にウィンドウを閉じる処理がコピペ元のGoogleのサンプルコードに組み込まれていたからでした。

chromium.googlesource.com

余談ですが、Chrome Native Messagingのマニフェストに指定した大元のWindowsバッチファイルから @echo off を削除すると、コネクションが確立した瞬間に切断されるようになりました。今まで全然意識していませんでしたが、コマンドプロンプトのECHOが <ON> の場合のエコーバックって標準出力に吐いてるんですね。言われてみたらそうか。