Register your handler with signal.signal like this:
#!/usr/bin/env python
import signal
import sys
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()
Code adapted from here.
More documentation on signal can be found here.
Register your handler with signal.signal like this:
#!/usr/bin/env python
import signal
import sys
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()
Code adapted from here.
More documentation on signal can be found here.
You can treat it like an exception (KeyboardInterrupt), like any other. Make a new file and run it from your shell with the following contents to see what I mean:
import time, sys
x = 1
while True:
try:
print x
time.sleep(.3)
x += 1
except KeyboardInterrupt:
print "Bye"
sys.exit()
Signal handler is registered, but process still exits abruptly on Ctrl+C (implementing graceful shutdown)
signals - Python: Catch Ctrl-C command. Prompt "really want to quit (y/n)", resume execution if no - Stack Overflow
Ctrl-C doesn't send SIGINT to python program launched with debugger
Invoke function before closing script (Ctrl + C)
Videos
Hello, I have a job processor written in Python. It's a loop that pulls a job from a database, does stuff with it inside a transaction with row-level locking, then writes the result back to the database. Jobs are relatively small, usually shorter than 5s.
import asyncio
import signal
running = True
def signal_handler(sig, frame):
global running
print("SIGINT received, stopping on next occasion")
running = False
signal.signal(signal.SIGINT, signal_handler)
while running:
asyncio.run(do_one_job()) # imported from a module
I would expect the above code to work. But when Ctrl+Cing the process, the current job stops abruptly with a big stack trace and an exception from one of the libraries used indirectly from do_one_job (urllib.ProtocolError: Connection aborted). The whole point of my signal handling is to avoid interrupting a job while it's running. While jobs are processed within transactions and shouldn't break the DB's consistency, I'd rather have an additional layer of safety by trying to wait until they are properly finished, especially since they're short.
Why can do_one_job() observe a signal that's supposed to be already handled? How can I implement graceful shutdown in Python?
The python signal handlers do not seem to be real signal handlers; that is they happen after the fact, in the normal flow and after the C handler has already returned. Thus you'd try to put your quit logic within the signal handler. As the signal handler runs in the main thread, it will block execution there too.
Something like this seems to work nicely.
import signal
import time
import sys
def run_program():
while True:
time.sleep(1)
print("a")
def exit_gracefully(signum, frame):
# restore the original signal handler as otherwise evil things will happen
# in raw_input when CTRL+C is pressed, and our signal handler is not re-entrant
signal.signal(signal.SIGINT, original_sigint)
try:
if raw_input("\nReally quit? (y/n)> ").lower().startswith('y'):
sys.exit(1)
except KeyboardInterrupt:
print("Ok ok, quitting")
sys.exit(1)
# restore the exit gracefully handler here
signal.signal(signal.SIGINT, exit_gracefully)
if __name__ == '__main__':
# store the original SIGINT handler
original_sigint = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, exit_gracefully)
run_program()
The code restores the original signal handler for the duration of raw_input; raw_input itself is not re-entrable, and re-entering it
will lead to RuntimeError: can't re-enter readline being raised from time.sleep which is something we don't want as it is harder to catch than KeyboardInterrupt. Rather, we let 2 consecutive Ctrl-C's to raise KeyboardInterrupt.
from https://gist.github.com/rtfpessoa/e3b1fe0bbfcd8ac853bf
#!/usr/bin/env python
import signal
import sys
def signal_handler(signal, frame):
# your code here
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
Bye!