Python の subprocess.run は KeyboardInterrupt で強制的に kill される
Python から Terraform をインタラクティブな(stdin をそのまま渡す)形で呼ぶために、subprocess.run を使って subprocess で走らせようとしている。
ここで、 Ctrl-C (KeyboardInterrupt) のハンドリングがネックになる。 Terraform は Ctrl-C を打つと Graceful な終了を試みることがあるので、Ctrl-C が打たれてもしばらく subprocess を終了しないようにしたい。
try で囲んで except KeyboardInterrupt すれば実現できそうだが、残念ながら subprocess.run や subprocess.call では実現できず、すぐに kill されてしまう。
Python 3.9 で確認。
subprocess.run 中に Ctrl-C を押すとどうなるのか
subprocess.run 中に Ctrl-C を押すと、まず subprocess.run 中で使われている Popen.communicate でハンドリングされる。
ここでは、0.25秒(固定値)*1待った後に KeyboardInterrupt を re-raise している。
その後、subprocess.run でハンドリングされるが、そこで有無を言わさず process.kill() が呼ばれて kill されてしまう。
- Popen が re-raise するまでの待ち時間を伸ばせないこと
- re-raise した場合
subprocess.runがprocess.kill()を呼んでしまうこと
から、 subprocess.run 中に Ctrl-C で subprocess が終了するのを防ぐ手段はない。
どうするか
subprocess.run の便利な機能を使えないのが残念だが、Popen を直接使うようにした。
with subprocess.Popen(args) as p: try: return_code = p.wait() except KeyboardInterrupt: # Ctrl-C が打たれたら、プロセスが終わるのを待ってから raise する # 補足: 2回目の p.wait() は try-except で囲んでいないので、もう一度 Ctrl-C が打たれた場合は特にハンドリングされず raise される return_code = p.wait() raise
*1:初回以降は wait しない。詳細: https://github.com/python/cpython/pull/5026