重置Python SIGINT为默认信号处理程序

26

版本信息:

  • 操作系统: Windows 7
  • Python版本: 3.3.5

下面是我正在尝试的一小段测试代码。目标是在执行某些代码时忽略按下 CTRL-C 键,之后再恢复 CTRL-C 的行为。

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!')

在我玩弄这个过程中,我观察到:

  • 标记1标记2之间发送的CTRL-C将由异常处理程序处理(正如预期)。
  • 标记2标记3之间发送的CTRL-C被忽略(仍然符合预期)。
  • 标记3之后发送的CTRL-C会被处理,但不会跳转到异常处理程序。相反,Python会立即终止

还要考虑以下内容:

>>>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

最初,尽管信号处理程序被认为是默认信号处理程序,但似乎与SIG_DFL定义的处理程序不同。

如果有人能够阐明这一点,特别是在将信号处理程序恢复为SIG_DFL后忽略异常处理程序时。


1
Windows 上的 SIGINT 存在问题,请参见 https://dev59.com/M3LYa4cB1Zd3GeqPXWYD 和 http://bugs.python.org/issue18040。 - cdarke
1个回答

41
Python会安装自己的SIGINT处理程序,以触发KeyboardInterrupt异常。将信号设置为SIG_DFL不会恢复该处理程序,而是恢复系统本身的“标准”处理程序(终止解释器)。
您需要存储原始处理程序,并在完成后恢复该处理程序:
original_sigint_handler = signal.getsignal(signal.SIGINT)

# Then, later...
signal.signal(signal.SIGINT, original_sigint_handler)

正如kindall在评论中所说,您可以将其表达为上下文管理器

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)

您可以像这样使用它:
# 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)

16
请注意,默认的处理程序始终可用,名称为signal.default_int_handler - Eryk Sun
4
如果在try/finally语句中省略了except:块,Python会将异常上抛。 - Brandon LeBlanc
3
另外请注意,signal.signal()返回先前的信号处理程序,因此单独调用signal.getsignal()是不必要的。 - Simon G.
好观点@SimonG。花了我一些时间才明白,确实original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)就可以解决问题。 - lcnittl
没有结果 :(。这部分文档写得相当糟糕。https://docs.python.org/3/search.html?q=default_int_handler - Myles Prather

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接