9
\$\begingroup\$

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?

asked Jun 16, 2014 at 7:33
\$\endgroup\$

2 Answers 2

8
\$\begingroup\$

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.

answered Jun 16, 2014 at 7:56
\$\endgroup\$
5
  • 1
    \$\begingroup\$ Hm, this looks much more pythonic :) \$\endgroup\$ Commented Jun 16, 2014 at 7:59
  • \$\begingroup\$ See also: contextlib.contextmanager, which allows you to use the with statement above without explicitly implementing __enter__ and __exit__. \$\endgroup\$ Commented Jun 16, 2014 at 8:09
  • \$\begingroup\$ @Nobody or #codesparkle can you provide simple example of signal handling which is implemented with "with" statement? \$\endgroup\$ Commented Apr 22, 2015 at 7:38
  • \$\begingroup\$ @Phoenix: If you did not find an answer to your question yet, feel free to ask it on the appropriate SE site (probably SO). Codereview is the wrong platform for this question. However, you should note that the signal handling itself is not done via with. It is only used to set and reset the signal handlers that do the actual work (which might or might not involve a with statement). \$\endgroup\$ Commented Apr 22, 2015 at 17:27
  • \$\begingroup\$ This is just brilliant. \$\endgroup\$ Commented Apr 2, 2018 at 16:15
2
\$\begingroup\$

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
answered Jun 16, 2014 at 7:52
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.