Reputation: 61
I have a python file parent.py as a wrapper to launch another python program child.py. This is the main part of parent.py:
p = subprocess.run(f"python -u {cmd} 2>&1 | tee -a {file_path}", shell=True)
I want to use ctrl-c to control the child process. So in the child.py, I wrote a try-except to catch KeyBoardInterrupt and handle it:
try:
while True:
pass
except KeyboardInterrupt:
print("lalala~~~~~~~~~~~~~~~~~~~~~")
However, if I run the parent.py and then press ctrl-c, the parent has been terminated and the child.py cannot handle the interrupt (no "lalala" output):
^CTraceback (most recent call last): File "/home/zxl/rcd_python1.py", line 27, in p = subprocess.run(f"python -u {cmd} 2>&1 | tee -a {file_path}", shell=True) File "/home/zxl/programs/anaconda3/envs/pyg2/lib/python3.8/subprocess.py", line 495, in run stdout, stderr = process.communicate(input, timeout=timeout) File "/home/zxl/programs/anaconda3/envs/pyg2/lib/python3.8/subprocess.py", line 1020, in communicate self.wait() File "/home/zxl/programs/anaconda3/envs/pyg2/lib/python3.8/subprocess.py", line 1083, in wait return self._wait(timeout=timeout) File "/home/zxl/programs/anaconda3/envs/pyg2/lib/python3.8/subprocess.py", line 1808, in _wait (pid, sts) = self._try_wait(0) File "/home/zxl/programs/anaconda3/envs/pyg2/lib/python3.8/subprocess.py", line 1766, in _try_wait (pid, sts) = os.waitpid(self.pid, wait_flags) KeyboardInterrupt
How can I send the ctrl-c to the subprocess and make it handle it, instead of terminate everything? Thanks a lot~
Upvotes: 0
Views: 165
Reputation: 58681
This is working already.
When you press Ctrl-C, the terminal driver sends a SIGINT to every member of the foreground process group. [1] In your case, that means each of four processes receives a SIGINT:
main.py
, it's child shell (subshell=True), and that shell's children child.py
and tee
.
Your child.py
correctly handles the SIGINT and prints "lalalala~~~~"
. However, this output is piped to tee
, meaning you'd never see it on the console. Additionally, tee
is itself killed by a SIGINT, almost certainly before it has a chance to read from its input and write to its output. Your main.py
, since it doesn't handle the SIGINT, dies with the stack trace you observe.
Upvotes: 1
Reputation: 89
I am getting the output you expect. Your code is working for me.
test_subprocess.py
import time
if __name__ == '__main__':
while True:
try:
time.sleep(1)
except KeyboardInterrupt:
print("lalala")
test_subprocess_main.py
import subprocess
if __name__ == '__main__':
print('Start')
p = subprocess.run("python3 test_subprocess.py", shell=True, check=True)
The output.
Mac-sashi:fun sashi$ python3 test_subprocess_main.py
Start
^Clalala
Traceback (most recent call last): File "/Users/sashi/repos/fun/test_subprocess_main.py", line 6, in p = subprocess.run("python3 test_subprocess.py", shell=True, check=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/sashi/.pyenv/versions/3.11.1/lib/python3.11/subprocess.py", line 550, in run stdout, stderr = process.communicate(input, timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/sashi/.pyenv/versions/3.11.1/lib/python3.11/subprocess.py", line 1199, in communicate self.wait() File "/Users/sashi/.pyenv/versions/3.11.1/lib/python3.11/subprocess.py", line 1262, in wait return self._wait(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/sashi/.pyenv/versions/3.11.1/lib/python3.11/subprocess.py", line 1997, in _wait (pid, sts) = self._try_wait(0) ^^^^^^^^^^^^^^^^^ File "/Users/sashi/.pyenv/versions/3.11.1/lib/python3.11/subprocess.py", line 1955, in _try_wait (pid, sts) = os.waitpid(self.pid, wait_flags) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ KeyboardInterrupt
Can you try the code I posted? Does it work for you?
Upvotes: 0