Version info:
Below is small piece of test code I was playing around with. The aim was to ignore CTRL-C
being pressed while certain code was being executed, after which the CTRL-C
behaviour would be restored.
import signal
import time
try:
# marker 1
print('No signal handler modifications yet')
print('Sleeping...')
time.sleep(10)
# marker 2
signal.signal(signal.SIGINT, signal.SIG_IGN)
print('Now ignoring CTRL-C')
print('Sleeping...')
time.sleep(10)
# marker 3
print('Returning control to default signal handler')
signal.signal(signal.SIGINT, signal.SIG_DFL)
print('Sleeping...')
time.sleep(10)
except KeyboardInterrupt:
print('Ha, you pressed CTRL-C!')
What I've observed while playing around with this:
CTRL-C
sent between marker 1 and marker 2 will be processed by the exception handler (as expected).CTRL-C
sent between marker 2 and marker 3 is ignored (still, as expected)CTRL-C
sent after marker 3 is processed but will not jump to the exception handler. Instead, Python just terminates immediately.Also, consider this:
>>>import signal
>>>signal.getsignal(signal.SIGINT)
<built-in function default_int_handler>
>>> signal.getsignal(signal.SIGINT) is signal.SIG_DFL
False
>>> signal.signal(signal.SIGINT, signal.SIG_DFL)
<built-in function default_int_handler>
>>> signal.getsignal(signal.SIGINT) is signal.SIG_DFL
True
So initially, while the signal handler is considered the default signal handler, it seems to be a different handler than the one defined by SIG_DFL
.
If anyone could shed some light on this, especially while the exception handler is ignored after restoring the signal handler to SIG_DFL
.
Python installs its own SIGINT
handler in order to raise KeyboardInterrupt
exceptions. Setting the signal to SIG_DFL
will not restore that handler, but the "standard" handler of the system itself (which terminates the interpreter).
You have to store the original handler and restore that handler when you're done:
original_sigint_handler = signal.getsignal(signal.SIGINT)
# Then, later...
signal.signal(signal.SIGINT, original_sigint_handler)
As kindall rightfully says in comments, you can express this as a context manager:
from contextlib import contextmanager
@contextmanager
def sigint_ignored():
original_sigint_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, signal.SIG_IGN)
try:
print('Now ignoring CTRL-C')
yield
except:
raise # Exception is dropped if we don't reraise it.
finally:
print('Returning control to default signal handler')
signal.signal(signal.SIGINT, original_sigint_handler)
You can use it like this:
# marker 1
print('No signal handler modifications yet')
print('Sleeping...')
time.sleep(10)
# marker 2
with sigint_ignored():
print('Sleeping...')
time.sleep(10)
# marker 3
print('Sleeping...')
time.sleep(10)