如何在 Python 中捕获 SIGINT?

2024-11-29 08:41:00
admin
原创
11
摘要:问题描述:我正在编写一个启动多个进程和数据库连接的 Python 脚本。有时我想用Ctrl+C信号终止该脚本,并且想进行一些清理。在 Perl 中我会这样做:$SIG{'INT'} = 'exit_gracefully'; sub exit_gracefully { print "Caug...

问题描述:

我正在编写一个启动多个进程和数据库连接的 Python 脚本。有时我想用Ctrl+C信号终止该脚本,并且想进行一些清理。

在 Perl 中我会这样做:

$SIG{'INT'} = 'exit_gracefully';

sub exit_gracefully {
    print "Caught ^C 
";
    exit (0);
}

我怎样在 Python 中做类似这样的事情?


解决方案 1:

像这样注册你的处理程序signal.signal

#!/usr/bin/env python
import signal
import sys

def signal_handler(sig, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()

代码改编自此处。

更多文档可以在这里signal找到。
 

解决方案 2:

您可以将其视为异常 ( KeyboardInterrupt),就像任何其他异常一样。创建一个新文件并从 shell 运行它,其中包含以下内容,以了解我的意思:

import time, sys

x = 1
while True:
    try:
        print x
        time.sleep(.3)
        x += 1
    except KeyboardInterrupt:
        print "Bye"
        sys.exit()

解决方案 3:

作为上下文管理器:

import signal

class GracefulInterruptHandler(object):

    def __init__(self, sig=signal.SIGINT):
        self.sig = sig

    def __enter__(self):

        self.interrupted = False
        self.released = False

        self.original_handler = signal.getsignal(self.sig)

        def handler(signum, frame):
            self.release()
            self.interrupted = True

        signal.signal(self.sig, handler)

        return self

    def __exit__(self, type, value, tb):
        self.release()

    def release(self):

        if self.released:
            return False

        signal.signal(self.sig, self.original_handler)

        self.released = True

        return True

使用方法:

with GracefulInterruptHandler() as h:
    for i in xrange(1000):
        print "..."
        time.sleep(1)
        if h.interrupted:
            print "interrupted!"
            time.sleep(2)
            break

嵌套处理程序:

with GracefulInterruptHandler() as h1:
    while True:
        print "(1)..."
        time.sleep(1)
        with GracefulInterruptHandler() as h2:
            while True:
                print "    (2)..."
                time.sleep(1)
                if h2.interrupted:
                    print "    (2) interrupted!"
                    time.sleep(2)
                    break
        if h1.interrupted:
            print "(1) interrupted!"
            time.sleep(2)
            break

来自这里: https: //gist.github.com/2907502

解决方案 4:

您可以通过捕获异常来处理CTRL+ 。您可以在异常处理程序中实现任何清理代码。C`KeyboardInterrupt`

解决方案 5:

另一个片段

称为main主要函数和+处理exit_gracefully程序Ctrl`C`

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pass
    finally:
        exit_gracefully()

解决方案 6:

来自Python的文档:

import signal
import time

def handler(signum, frame):
    print 'Here you go'

signal.signal(signal.SIGINT, handler)

time.sleep(10) # Press Ctrl+c here

解决方案 7:

如果您想确保您的清理过程完成,我会在Matt J 的答案中添加SIG_IGN,以便SIGINT忽略进一步的内容,从而防止您的清理过程被中断。

import signal
import sys

def signal_handler(signum, frame):
    signal.signal(signum, signal.SIG_IGN) # ignore additional signals
    cleanup() # give your process a chance to clean up
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler) # register the signal with the signal handler first
do_stuff()

解决方案 8:

我改编了@udi 的代码来支持多种信号(没什么特别的):

class GracefulInterruptHandler(object):
    def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
        self.signals = signals
        self.original_handlers = {}

    def __enter__(self):
        self.interrupted = False
        self.released = False

        for sig in self.signals:
            self.original_handlers[sig] = signal.getsignal(sig)
            signal.signal(sig, self.handler)

        return self

    def handler(self, signum, frame):
        self.release()
        self.interrupted = True

    def __exit__(self, type, value, tb):
        self.release()

    def release(self):
        if self.released:
            return False

        for sig in self.signals:
            signal.signal(sig, self.original_handlers[sig])

        self.released = True
        return True

此代码支持键盘中断调用(SIGINT)和SIGTERMkill <process>

解决方案 9:

与Matt J的回答相反,我使用了一个简单的对象。这使我能够将此处理程序解析为所有需要安全停止的线程。

class SIGINT_handler():
    def __init__(self):
        self.SIGINT = False

    def signal_handler(self, signal, frame):
        print('You pressed Ctrl+C!')
        self.SIGINT = True


handler = SIGINT_handler()
signal.signal(signal.SIGINT, handler.signal_handler)

别处

while True:
    # task
    if handler.SIGINT:
        break

解决方案 10:

您可以使用 Python 内置信号模块中的函数在 Python 中设置信号处理程序。具体来说,该signal.signal(signalnum, handler)函数用于为handler信号注册函数signalnum

解决方案 11:

感谢现有的答案,但补充signal.getsignal()

import signal

# store default handler of signal.SIGINT
default_handler = signal.getsignal(signal.SIGINT)
catch_count = 0

def handler(signum, frame):
    global default_handler, catch_count
    catch_count += 1
    print ('wait:', catch_count)
    if catch_count > 3:
        # recover handler for signal.SIGINT
        signal.signal(signal.SIGINT, default_handler)
        print('expecting KeyboardInterrupt')

signal.signal(signal.SIGINT, handler)
print('Press Ctrl+c here')

while True:
    pass

解决方案 12:

就我个人而言,我无法使用 try/except KeyboardInterrupt,因为我使用的是阻塞的标准套接字 (IPC) 模式。因此 SIGINT 被提示,但仅在套接字上接收到数据后才出现。

设置信号处理程序的行为相同。

另一方面,这仅适用于实际终端。其他启动环境可能不接受Ctrl+ C,或预先处理信号。

此外,Python 中还有“Exceptions”和“BaseExceptions”,它们的不同之处在于解释器需要自行干净地退出,因此某些异常比其他异常具有更高的优先级(Exceptions 源自 BaseException)

解决方案 13:

以下是一行代码(减去导入),已使用 Python 3.10 测试(不确定其他版本):

#!/usr/bin/env python3
from signal import SIGINT, SIGTERM, sigwait


print('received', sigwait({SIGINT, SIGTERM}))

但是,如果在 INT/TERM 之前发送另一个信号(例如 USR1),行为会很有趣。sigwait 将等待 INT/TERM,但不会按预期输出“收到 {信号}”行。

$ ./test.py # ctrl + c
^Creceived Signals.SIGINT

$ ./test.py # kill -TERM <pid>
received Signals.SIGTERM

$ ./test.py # kill -USR1 <pid> ; kill -TERM <pid>
User defined signal 1: 30
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   657  
  如何借鉴华为IPD体系优化企业研发?在当今竞争激烈的市场环境中,企业要想保持技术领先和产品竞争力,必须拥有一套高效且严谨的研发管理体系。华为作为全球领先的ICT解决方案提供商,其集成产品开发(IPD, Integrated Product Development)体系与质量管理体系(如ISO 9000系列)的融合实践,...
IPD项目管理   15  
  IPD流程图的7种经典绘制方法详解在产品开发领域,集成产品开发(Integrated Product Development,简称IPD)流程被广泛应用,以提高产品开发的效率和质量。IPD流程图作为这一流程的可视化工具,其绘制方法至关重要。本文将详细介绍七种经典的IPD流程图绘制方法,帮助项目管理人员和团队更好地理解和...
IPD研发管理体系   18  
  IPD流程:企业创新管理的核心引擎在当今快速变化的市场环境中,企业要想持续保持竞争力,就必须不断进行创新。而IPD(Integrated Product Development,集成产品开发)流程作为一种先进的产品开发管理模式,正逐渐成为众多企业提升创新能力、加速产品上市速度、降低开发成本的重要选择。本文将深入探讨IP...
IPD管理   18  
  IPD流程与传统产品开发流程的概述在产品开发领域,企业不断寻求高效、系统的管理方法以确保产品能够顺利从概念转化为市场成功的产品。集成产品开发(Integrated Product Development,简称IPD)流程与传统产品开发流程是两种截然不同的管理理念和方法。传统产品开发流程往往以职能部门为核心,各部门按顺序...
IPD流程中PDCP是什么意思   16  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用