How to stop a console process on Windows using Python
— 2 min read
Background
- In Python, we use
p = subprocess.Popen(cmd)
to start a command and callp.terminate()
to stop it. But for some commands on Windows, e.g.ping 127.0.0.1 -n 9999
, terminate it will "force stop" it without producing the "ping statistics" in the end. These processes need to be stopped by the CTRL-C event in order to allow them finish some "cleanups" nicely.
Discusses
- There've been lots of discusses around this topic.
- FIXME: fix all links
- See Sending ^C to Python subprocess objects on Windows - Stack Overflow and signals - Can I send a ctrl-C (SIGINT) to an application on Windows? - Stack Overflow for solutions
- Also GenerateConsoleCtrlEvent function - Windows Console | Microsoft Docs and subprocess — Subprocess management — Python 3.9.2 documentation for some official info
Summary
- Two solutions
- Solution 1, use
subprocess.send_signal(signal.CTRL_C_EVENT)
and ignore the KeyboardInterrupt exception at the caller side- The key is to handle the side effects of the CTRL-C event, a simple solution is to wait in the caller process until child process exited.
- example code
# stop the processp.send_signal(signal.CTRL_C_EVENT)# wait in the caller process to ignore the KeyboardInterrupttry:# needs to be longer than the time costs that child process requires to exittime.sleep(10)except KeyboardInterrupt:print("Ignored keyboard interrupt")
- Cons
- The CTRL-C event is sent to the whole process group, which means if your python main process starts a lot of processes, e.g. started multiple
ping -n 9999
commands, cancel either one will result in all processes (which have the same process group and have not ignored the KeyboardInterrupt exception) exited earlier than expected.
- The CTRL-C event is sent to the whole process group, which means if your python main process starts a lot of processes, e.g. started multiple
- In short, this is a solution for simple applications, or applications don't have multiple child processes with the same process group at the same time.
- Solution 2, use some "Windows hacks"
- The key the to completely avoid CTRL-C message being spread to other processes.
- Starts a new command, which will free the console, attach to the target console, and fires the CTRL-C event.
- See example code shared in Sending ^C to Python subprocess objects on Windows - Stack Overflow
- FIXME: add portal here.
- Solution 1, use
The final solution
- I've created a console-ctrl project python package based on the Solution 2, use some "Windows hacks" above.
- FIXME: add portal here.
- See README for details.