如何使Python上下文管理器捕获SIGINT或SIGTERM信号

8
我使用上下文管理器来传输数据,以便在程序退出时关闭连接。 我将我的程序作为后台的守护程序运行。
如何使上下文管理器在守护程序被SIGINT或SIGTERM或kill命令发送的任何中断信号所中断时进行处理?
我在Raspberry Pi和Ubuntu上运行Python 3。
我看到了这个: How do I capture SIGINT in Python? 这对我很有帮助,但我不确定如何将其与Python的上下文管理器一起使用?也就是说,假设我已经构建了一个对象作为上下文管理器:
class Sensor:

    def __init__(self, name: str):
        self.name = name

    def __enter__(self):
        self._connect()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

在作为守护进程运行的脚本中,我使用了该对象。有没有一种Pythonic的方法来指定要在 SIGINTSIGTERM 异常上调用__exit__函数呢?


1
这个回答解决了你的问题吗?如何在Python中捕获SIGINT信号? - mkrieger1
不完全是,我已经在那方面更新了问题。 - gohu
你的上下文管理器已经适用于 SIGINT 了吧? - Davis Herring
你需要注册任何你想要作为信号处理程序调用的函数,例如 self.close - mkrieger1
是的,因为它是一个上下文管理器,所以它可能已经按照您的期望工作而无需执行该操作。您能展示一个 [mre] 吗?在使用 kill 命令时脚本没有按照您的期望工作。 - mkrieger1
我已经尝试了一些使用信号的东西。更复杂的是,我使用线程来流式传输和写入数据。我将更新我的答案,看看是否有更好的想法可尝试。 - gohu
1个回答

4

你可以为你的上下文管理器创建一个内部方法(例如_handle_interrupt),该方法调用sys.exit()并在调用__enter__后将其注册为信号处理程序:

class Sensor:

    def __init__(self, name: str):
        self.name = name

    def _handle_interrupt(self):
       sys.exit()  # will trigger a exception, causing __exit__ to be called

    def __enter__(self):
        signal.signal(signal.SIGINT, self._handle_interrupt)
        signal.signal(signal.SIGTERM, self._handle_interrupt)
        self._connect()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

你也应该查看一下PEP 419,它可能对你的设置有帮助。

至于关于线程的要求,没有更多信息很难判断。


这个如何与https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager一起使用? - AlexM
我猜你可以使用上面的代码并扩展ContextDecorator,这将允许你将其作为装饰器与任何函数一起使用。 - nehtor.t

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