Feature Request: Handling of ctrl-c while running child process
Closed this issue · 2 comments
I have found this repository in an internet search. It is helpful. Thank you for writing it.
Do you have suggestions on how to handle ctrl c when running a subprocess where the subprocess wants to react and do something before it exits?
Here is an example.
outer.sh
echo start outer
python inner.py
echo end outerinner.py
import time
print("start inner")
try:
time.sleep(5)
except KeyboardInterrupt:
print("got ctrl c inner")
time.sleep(1)
print("finished handling ctrl c inner")
print("end inner")Running this without interrupting outputs
start outer
start inner
end inner
end outer
and with ctrl c
start outer
start inner
^Cgot ctrl c inner
finished handling ctrl c inner
end inner
end outer
but if we use this version of outer, outer.py
import subprocess
print("start outer")
subprocess.run(["python", "inner.py"], check=True)
print("end outer")and ctrl c we get
start outer
start inner
^Cgot ctrl c inner
Traceback (most recent call last):
File "outer.py", line 4, in <module>
subprocess.run(["python", "inner.py"], check=True)
File "/usr/lib/python3.7/subprocess.py", line 474, in run
stdout, stderr = process.communicate(input, timeout=timeout)
File "/usr/lib/python3.7/subprocess.py", line 931, in communicate
self.wait()
File "/usr/lib/python3.7/subprocess.py", line 990, in wait
return self._wait(timeout=timeout)
File "/usr/lib/python3.7/subprocess.py", line 1624, in _wait
(pid, sts) = self._try_wait(0)
File "/usr/lib/python3.7/subprocess.py", line 1582, in _try_wait
(pid, sts) = os.waitpid(self.pid, wait_flags)
KeyboardInterrupt
The inner process was not able to react to the ctrl c.
There are various ways to attempt to fix this like catching the KeyboardInterrupt or the SIGTERM also in the outer process and calling terminate on the inner but I am not sure what the best approach is.
I'm not sure what the best is either!
It looks like if an error is caught in the outer process while waiting for subprocess.run to complete, SIGTERM is sent internally. KeyboardInterrupt is raise for SIGINT, so terminate just ends the process immediately. You could use the signal.signal to install a handler for SIGTERM in inner.py, but this isn't a very generic solution because you need to be the author of the program in the inner process to get it to work.
This is what I came up with:
import subprocess
print("start outer")
proc = subprocess.Popen(["python", "inner.py"])
while True:
try:
ret_code = proc.wait()
except KeyboardInterrupt:
pass
else:
break
print("end outer")Creating a process with Popen means an interrupt when calling .wait() will not terminate the inner program. I put it in a loop so the inner process would get as many chances as it needs to handle the keyboard interrupt.
Again, I'm not promising that this is the "best" approach, but it's what I came up with and I would be comfortable using it in my own code.
Thanks for your answer. Feel free to close whenever.