When building a Python script that is long running or has to manage some state on termination it is helpful to handle a couple of signals:
SIGINT
: The signal sent when pressing Ctrl+CSIGTERM
andSIGQUIT
: Meant to terminate the process, sent bykill
and process managers like systemd or supervisord
Handling them is possible with Pythons signals
library:
import signals
class SignalHandler:
stop = False
def __init__(self):
# Ctrl+C
signal.signal(signal.SIGINT, self.exit_gracefully)
# Supervisor/process manager signals
signal.signal(signal.SIGTERM, self.exit_gracefully)
signal.signal(signal.SIGQUIT, self.exit_gracefully)
def exit_gracefully(self, *args):
self.stop = True
Example 1: Simple loop
The simplest example is using the SignalHandler
as the condition on the loop
and have code to stop gracefully below the loop.
print("Starting program")
signal_handler = SignalHandler()
while not signal_handler.stop:
do_something()
print("Saving state and stopping gracefully")
Example 2: Loop with sleep
Sometimes a long running task sleeps for long amounts of time, which makes it impossible to react to the loop condition.
In this case the simplest solution is to break up the large sleep into shorter sleep intervals
and check SignalHandler
in between sleeping.
import time
TOTAL_SLEEP = 60
print("Starting program")
signal_handler = SignalHandler()
while not signal_handler.stop:
do_something()
# Instead of time.sleep(TOTAL_SLEEP)
for _ in range(0, TOTAL_SLEEP):
time.sleep(1)
if signal_handler.stop:
break
print("Saving state and stopping gracefully")
The break
then brings us out into the while
loop,
ending the current iteration and forcing a new check on the while
loop,
therefore ending it and allowing the program to end.