It's because of the design of the Python interpreter and interactive session.
Ctrl + C sends a signal, SIGINT, to the Python process, which the Python interpreter handles by raising the KeyboardInterrupt exception in the currently-running scope.
If the interpreter is running in an interactive session (i.e. by running python or python3 at the console), then the exception in the current function is printed and you return to the Python prompt. If the interpreter is running a script (e.g. by python3 my_script.py), then unless the KeyboardInterrupt is handled by the script, the whole program will stop when the exception is raised.
It's because of the design of the Python interpreter and interactive session.
Ctrl + C sends a signal, SIGINT, to the Python process, which the Python interpreter handles by raising the KeyboardInterrupt exception in the currently-running scope.
If the interpreter is running in an interactive session (i.e. by running python or python3 at the console), then the exception in the current function is printed and you return to the Python prompt. If the interpreter is running a script (e.g. by python3 my_script.py), then unless the KeyboardInterrupt is handled by the script, the whole program will stop when the exception is raised.
It's worth pointing out from the docs that you need to handle KeyboardInterrupt explicitly, even if you already have a catch for Exception:
The exception inherits from BaseException so as to not be accidentally caught by code that catches Exception and thus prevent the interpreter from exiting.
Ctrl+C not working when using input on Windows
Ctrl-C doesn't send SIGINT to python program launched with debugger
About ctrl C not working in terminal. - Apple Community
Stopping Python using Ctrl + C - Stack Overflow
On Windows, in the interactive Python interpreter, the options to exit are:
quit()exit()Ctrl + Z then Enter
Ctrl + Break
When running scripts, Ctrl + C can generally be used to send a KeyboardInterrupt that halts script execution (note that a Traceback will be generated).
The Ctrl + Break option assumes your keyboard has a Break or Pause/Break key.
On Linux, you can use
- the commands
quit()orexit() - or the key combination Ctrl+D
Pressing Ctrl+C in the Python Interpreter only causes a KeyboardInterrupt exception.
Pressing Ctrl+Break in Linux does nothing relevant in regard to the Python shell.
Pressing Ctrl+Z merely stops and puts the Python process to the background without ending it.
On Windows, the only sure way is to use Ctrl + Break. It stops every Python script instantly!
(Note that on some keyboards, "Break" is labeled as "Pause".)
Pressing Ctrl + C while a Python program is running will cause the Python interpreter to raise a KeyboardInterrupt exception.
It's likely that a program that makes lots of HTTP requests will have lots of exception handling code. If the except part of the try-except block doesn't specify which exceptions it should catch, it will catch all exceptions including the KeyboardInterrupt that you just caused. A properly coded Python program will make use of the Python exception hierarchy and only catch exceptions that are derived from Exception.
# This is the wrong way to do things
try:
# Some stuff might raise an I/O exception
except:
# Code that ignores errors
# This is the right way to do things
try:
# Some stuff might raise an I/O exception
except Exception:
# This won't catch KeyboardInterrupt
If you can't change the code (or need to kill the program so that your changes will take effect) then you can try pressing Ctrl + C rapidly. The first of the KeyboardInterrupt exceptions will knock your program out of the try block and hopefully one of the later KeyboardInterrupt exceptions will be raised when the program is outside of a try block.
CTRL + C sends an SIGINT signal to the active program and the program you are trying to terminate is not the active program at the time.
In such cases I like to save the process id in a file and later kill the process with with that process id.
To do so, I create 2 files, a start.sh and a stop.sh. I start my script using start.sh and stop it using the stop.sh
The start.sh
#!/bin/bash
echo "Starting my script..."
nohup python /link/to/script.py > /link/to/script.process.log 2>&1&
echo $! > /link/to/script.process.pid
echo "My script is now running."
The stop.sh
#!/bin/bash
echo "Stopping my script..."
kill -9 `cat /link/to/script.process.pid`
echo "My script is now stopped."
If this is the only Python script you have running try
sudo killall python
or in extreme cases
sudo killall -9 python
You would need to give details of your script to know why ctrl-c sometimes works.
I have a question about sending "ctrl-c" type events to stdin on Windows. I googled around and even StackOverflow was full of dead ends.
Let's consider a simple program:
import subprocess TSHARK_PATH = "C:\\Program Files\\Wireshark\\tshark.exe" OUTPUT_FILE_NAME = "captured_packets.pcap" OUTPUT_DIR = "C:\\captured_packets_dir\\" INTERFACE_NO = "1" #as per "tshark -D" PACKET_FILTER = "icmp" # just an example OUTPUT_FILE_SIZE_LIMIT = "200" #in kB # here we do some preparation tshark = subprocess.Popen([TSHARK_PATH, "-i", INTERFACE_NO, "-b", "filesize:"+OUTPUT_FILE_SIZE_LIMIT, "-f", PACKET_FILTER, "-w", OUTPUT_DIR+OUTPUT_FILE_NAME]) # packet capturing is running, we do some other actions tshark.terminate() # do some post processing using captured files
Here is the problem:
-
If I do the same from console (manually) everything is fine -> but the Tshark is stopped by ctrl-c
-
If I do this from Python script (automatically) I get an "partial packet" error -> the Tshark is stopped by terminate()
Is there a way to send "ctrl-c" to the process using Subprocess? I know that Popen can be run with stdin=subprocess.PIPE, but simple write() won't do the trick.
Or maybe I'm doing this backwards and this is not the right solution to do this? I'm using Tshark because it's easier to use this tool for all the heavy lifting.
Windows seem to be rather unfriendly towards Python automation of command line tools.
This works:
import subprocess
import time
import win32api
import win32con
proc = subprocess.Popen("ping -t localhost", stdin=subprocess.PIPE)
time.sleep(3) # just so it runs for a while
print "sending ctrl c"
try:
win32api.GenerateConsoleCtrlEvent(win32con.CTRL_C_EVENT, 0)
proc.wait()
except KeyboardInterrupt:
print "ignoring ctrl c"
print "still running"
It sends ctrl-c to all processes that share the console of the calling process but then ignores it in the python process with an exception handler. You can't target the subprocess directly because that signal cannot be generated for process groups.
ctypes version
import subprocess
import time
import ctypes
proc = subprocess.Popen("ping -t localhost", stdin=subprocess.PIPE)
time.sleep(3) # just so it runs for a while
try:
ctypes.windll.kernel32.GenerateConsoleCtrlEvent(0, 0)
proc.wait()
except KeyboardInterrupt:
print "ignoring ctrlc"
print "still running"
ctrl-break version
This can target the process specifically but with ping ctrl-break doesn't actually cause it to terminate. You may be able to use it with tshark though.
import subprocess
import time
import ctypes
proc = subprocess.Popen("ping -t localhost", stdin=subprocess.PIPE,
creationflags=512) # CREATE_NEW_PROCESS_GROUP = 512
time.sleep(3) # just so it runs for a while
ctypes.windll.kernel32.GenerateConsoleCtrlEvent(1, proc.pid) # CTRL_BREAK_EVENT = 1
proc.wait()
On Unix, the terminate() functions sends SIGINT, which allows the process to clean up after itself before exiting, while the kill() method sends SIGKILL which ends it immediately. However on Windows both methods call the Windows API function TerminateProcess which straight up kills the process and does not allow cleanup, so tshark never gets a chance to write its files. See here and here.
I'm not much of a Windows expert, but have you tried os.kill(process.pid, signal.CTRL_C_EVENT)?
EDIT: Looking at the documentation for subprocess, it also looks like you need to create the process with the CREATE_NEW_PROCESS_GROUP flag to allow you to send signals to it. As an alternative to os.kill, there is also the slightly cleaner tshark.send_signal(signal.CTRL_C_EVENT).