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