How to process SIGTERM signal gracefully? How to process SIGTERM signal gracefully? python python

How to process SIGTERM signal gracefully?


A class based clean to use solution:

import signalimport timeclass GracefulKiller:  kill_now = False  def __init__(self):    signal.signal(signal.SIGINT, self.exit_gracefully)    signal.signal(signal.SIGTERM, self.exit_gracefully)  def exit_gracefully(self, *args):    self.kill_now = Trueif __name__ == '__main__':  killer = GracefulKiller()  while not killer.kill_now:    time.sleep(1)    print("doing something in a loop ...")     print("End of the program. I was killed gracefully :)")


First, I'm not certain that you need a second thread to set the shutdown_flag.
Why not set it directly in the SIGTERM handler?

An alternative is to raise an exception from the SIGTERM handler, which will be propagated up the stack. Assuming you've got proper exception handling (e.g. with with/contextmanager and try: ... finally: blocks) this should be a fairly graceful shutdown, similar to if you were to Ctrl+C your program.

Example program signals-test.py:

#!/usr/bin/pythonfrom time import sleepimport signalimport sysdef sigterm_handler(_signo, _stack_frame):    # Raises SystemExit(0):    sys.exit(0)if sys.argv[1] == "handle_signal":    signal.signal(signal.SIGTERM, sigterm_handler)try:    print "Hello"    i = 0    while True:        i += 1        print "Iteration #%i" % i        sleep(1)finally:    print "Goodbye"

Now see the Ctrl+C behaviour:

$ ./signals-test.py defaultHelloIteration #1Iteration #2Iteration #3Iteration #4^CGoodbyeTraceback (most recent call last):  File "./signals-test.py", line 21, in <module>    sleep(1)KeyboardInterrupt$ echo $?1

This time I send it SIGTERM after 4 iterations with kill $(ps aux | grep signals-test | awk '/python/ {print $2}'):

$ ./signals-test.py defaultHelloIteration #1Iteration #2Iteration #3Iteration #4Terminated$ echo $?143

This time I enable my custom SIGTERM handler and send it SIGTERM:

$ ./signals-test.py handle_signalHelloIteration #1Iteration #2Iteration #3Iteration #4Goodbye$ echo $?0


Here is a simple example without threads or classes.

import signalrun = Truedef handler_stop_signals(signum, frame):    global run    run = Falsesignal.signal(signal.SIGINT, handler_stop_signals)signal.signal(signal.SIGTERM, handler_stop_signals)while run:    pass # do stuff including other IO stuff