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?
2 Answers 2
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.
-
1\$\begingroup\$ Hm, this looks much more pythonic :) \$\endgroup\$Nobody moving away from SE– Nobody moving away from SE2014年06月16日 07:59:49 +00:00Commented 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\$Adam– Adam2014年06月16日 08:09:30 +00:00Commented 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\$Khamidulla– Khamidulla2015年04月22日 07:38:20 +00:00Commented 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 awith
statement). \$\endgroup\$Nobody moving away from SE– Nobody moving away from SE2015年04月22日 17:27:47 +00:00Commented Apr 22, 2015 at 17:27 -
\$\begingroup\$ This is just brilliant. \$\endgroup\$CVVS– CVVS2018年04月02日 16:15:27 +00:00Commented Apr 2, 2018 at 16:15
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