Handling signals with Python


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+C
  • SIGTERM and SIGQUIT: Meant to terminate the process, sent by kill 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.

python 

See also