如何正确处理SIGINT以关闭文件/连接

时间:2014-10-15 09:26:52

标签: python signals exit interrupt-handling

我想在我的脚本中实现正确的SIGINT处理,它会打开多个文件和数据库连接。如果脚本是CTRL + C'd或以其他方式中断,则应关闭这些。

以前我使用KeyboardInterrupt异常来捕获CTRL + C,我检查了文件/连接是否已定义,如果是这样,则关闭它们,然后退出。

这真的是pythonic方式吗,还是更好地建议使用信号处理程序? e.g。

import signal, sys, time

def handler(signum, frame):
    print("..kthxbye")
    sys.exit(1)

def main():
    signal.signal(signal.SIGINT, handler)
    i = 0
    while True:
        print(i)
        i += 1
        time.sleep(1)

if __name__ == "__main__":
    main()

对我来说这似乎是 clean ,但我不知道如何将文件名或数据库连接传递给处理程序。

2 个答案:

答案 0 :(得分:4)

我宁愿在主线程上捕获KeyboardInterrupt异常。 KeyboardInterrupt是python的默认SIGINT处理程序的结果。 KeyboardInterrupt异常的异常处理程序比直接捕获SIGINT时更安全/友好的上下文。

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        cleanup()

编辑:以下是如何在两种方法之间共享变量(状态):

<强>程序:

import sys, time

class SharedState:
    def __init__(self):
        self.var0 = 42
        self.var1 = 'string'

# method 1
shared_variable = 'woof woof'

# method 2: avoiding global declarations in functions
shared_state = SharedState()

def main():
    # In order to write a global variable you need a global
    # declaration otherwise the assignment would create a
    # local variable

    global shared_variable
    shared_variable = 5

    shared_state.var0 = 10

    time.sleep(10)

def cleanup():
    print shared_variable
    print shared_state.var0
    sys.exit(1)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        cleanup()

面向对象(我的偏好):

import sys, time

# method 3: object oriented programming
class Program:
    def __init__(self):
        self.var0 = 42
        self.var1 = 'string'

    def main(self):
        self.var0 = 5
        self.var1 = 'woof woof'
        time.sleep(10)

    def cleanup(self):
        # both main and cleanup can access the member
        # variables of this class
        print self.var0
        print self.var1
        sys.exit(1)

    def execute(self):
        try:
            self.main()
        except KeyboardInterrupt:
            self.cleanup()

if __name__ == '__main__':
    Program().execute()

答案 1 :(得分:1)

我的建议是使用signal库来处理信号。信号不是例外,它们是操作系统的Inter Process Communication(IPC)基础结构的一部分。

信号可以帮助您与程序通信,例如重新加载配置文件,在日志轮换期间关闭日志文件处理程序等等。像apache这样的大部分守护进程都会给它带来麻烦。

Shell脚本具有trap命令来处理信号并根据捕获的信号采取适当的操作。

通常,python会在退出时自动关闭所有文件处理程序和数据库连接。但为了安全起见,我们可以有一个函数来隐式处理它们。

下面的代码会捕获SIGINT并正确关闭文件。

import signal
import sys

die = False

def handler(signum, frame):
    global die
    print('Got SIGINT.')
    die = True

def closeFile(fh):
    fh.flush()
    fh.close()

signal.signal(signal.SIGINT, handler)

fh = open('/tmp/a.txt', 'w')

while True:
    data = input('> ')

    if data == 'q':
        closeFile(fh)
        break
    else:
        fh.write(data + '\n')

    if die:
        closeFile(fh)
        print('Completed cleanup.. ')
        sys.exit()