Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have code that needs to exit gracefully. To simplify things, I am presenting an example based on the answer given here.

Since I need to handle a few signals, I thought of putting this logic in a function:

def set_signals():
   original_sigint = signal.getsignal(signal.SIGINT)
   signal.signal(signal.SIGINT, exit_gracefully)
   signal.signal(signal.SIGTERM, exit_gracefully)
   signal.signal(signal.SIGINT, exit_gracefully)
   signal.signal(signal.SIGALRM, exit_gracefully)
   signal.signal(signal.SIGHUP, signal.SIG_IGN)

Thus, the main block of the python code should be:

if __name__ == '__main__':
    # store the original SIGINT handler
    set_signals()
    run_program()

This will fail however, since the exit_gracefully does not know the variable original_init.

Therefore, my solution was to create original_sigint as a global variable.

import signal
import time
import sys


original_sigint = None


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
    global original_sigint
    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)


def set_signals():
    global original_sigint
    original_sigint = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, exit_gracefully)
    signal.signal(signal.SIGTERM, exit_gracefully)
    signal.signal(signal.SIGINT, exit_gracefully)
    signal.signal(signal.SIGALRM, exit_gracefully)
    signal.signal(signal.SIGHUP, signal.SIG_IGN)


if __name__ == '__main__':
    # store the original SIGINT handler
    set_signals()
    run_program()

This approach works, but I am not happy with the use of another global variable (the code I working on is quite and is already littered with those). So the question is, how would you do it differently?

share|improve this question

2 Answers 2

up vote 4 down vote accepted

What about implementing your signal handling code inside a class? This could look something like the following:

class GracefulExit:
    def __enter__(self):
        # set up signals here
        # store old signal handlers as instance variables

    def __exit__(self, type, value, traceback):
        # restore old signal handlers

You can then use this in your code as follows:

with GracefulExit():
    # Signals will be caught inside this block.

# Signals will no more be caught here.

You'll find more examples of how to use the with-statement on the web.

share|improve this answer
1  
Hm, this looks much more pythonic :) –  Nobody Jun 16 at 7:59
    
See also: contextlib.contextmanager, which allows you to use the with statement above without explicitly implementing __enter__ and __exit__. –  codesparkle Jun 16 at 8:09

You can avoid the global by passing the original handler as function parameter and binding it with a lambda in set_signals:

def exit_gracefully(signum, frame, original_sigint):
#...

def set_signals():
    original_sigint = signal.getsignal(signal.SIGINT)
    bound_exit_gracefully = lambda signum, frame: exit_gracefully(signum, frame, original_sigint)
    signal.signal(signal.SIGINT, bound_exit_gracefully)
    signal.signal(signal.SIGTERM, bound_exit_gracefully)
    signal.signal(signal.SIGINT, bound_exit_gracefully)
    signal.signal(signal.SIGALRM, bound_exit_gracefully)
    signal.signal(signal.SIGHUP, signal.SIG_IGN)

The naming could also be improved a bit e.g.:

  • set_signals -> setup_grafecul_signal_handler
  • original_sigint -> original_sigint_handler
  • exit_gracefully -> gracefull_exit_signal_handler
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.