Question

Version info:

  • OS: Windows 7
  • Python version 3.3.5

Below is small piece of test code I was playing around. 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.

Was it helpful?

Solution

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top