通常使用with
语句打开文件,以便文件句柄不会泄露:
with open("myfile") as f:
…
但是如果异常发生在 open
调用中的某个地方怎么办? open
函数很可能不是Python解释器中的原子指令,因此完全有可能在KeyboardInterrupt
调用之前的某个时刻抛出异常异常,例如open
。完成,但系统调用完成后。
处理此问题的常规方法(例如,在POSIX信号中)使用屏蔽机制:在屏蔽时,异常的传递将暂停,直到它们稍后被取消屏蔽。这允许以open
之类的操作以原子方式实现。 Python中是否存在这样的原语?
[*]有人可能会说KeyboardInterrupt
并不重要,因为程序即将死亡,但并非所有程序都是如此。可以想象,程序可能会选择在顶层捕获KeyboardInterrupt
并继续执行,在这种情况下,泄漏的文件句柄会随着时间的推移而累加。
答案 0 :(得分:1)
我认为不可能屏蔽exceptions
,你可以屏蔽signals
而不是exceptions
。在您的情况下,KeyboardInterrupt
是引发signal.SIGINT
时引发的异常(即Ctrl + C)。
无法掩饰Exceptions
,因为它没有意义,对吧?假设您正在打开('file','r'),但file
不存在,这会导致open
函数抛出IOError
异常,我们应该无法mask
这些例外情况。掩盖它是没有意义的,因为在上述情况下,open永远无法完成。
例外 - 需要特殊处理的异常或异常情况
对于KeyboardInterrupt
异常,它的不同之处在于,就像我说的那样,它实际上是一个导致signal
异常的KeyboardInterrupt
。
使用函数mask
[Reference]
为此你必须将signal.pthread_sigmask
移动到另一个块,以便我们可以像context expression
这样的信号,运行上下文表达式来获取上下文管理器,然后{{1信号,示例代码看起来像(请注意我没有亲自测试过此代码) -
mask
答案 1 :(得分:0)
一些澄清:似乎异步异常在Python中并不常用。标准库仅记录KeyboardInterrupt
AFAIK。其他库可以实现自己的via信号处理程序,但我不认为(或希望?)这是一种常见的做法,因为异步异常非常难以使用。
以下是无法工作的天真解决方案:
try:
handle = acquire_resource(…)
except BaseException as e:
handle.release()
raise e
else:
with handle:
…
异常处理部分仍然容易受到异常的影响:在捕获异常但KeyboardInterrupt
完成之前,release
可能会再次发生{。<}}
try
语句的结尾与with
语句的开头之间也存在“差距”,因为它容易受到异常的影响。
我认为无论如何都不能让它以这种方式运作。
从不同的角度思考,似乎异步异常的唯一方式来自信号。如果这是真的,可以将它们掩盖为@AnandSKumar suggested。但是,屏蔽不可移植,因为它需要pthreads。
尽管如此,我们可以用一点点诡计伪装掩饰:
def masking_handler(queue, prev_handler):
def handler(*args):
queue.append(lambda: prev_handler[0](*args))
return handler
mask_stack = []
def mask():
queue = []
prev_handler = []
new_handler = masking_handler(queue, prev_handler)
# swap out the signal handler with our own
prev_handler[0] = signal.signal(signal.SIGINT, new_handler)
mask_stack.append((prev_handler[0], queue))
def unmask():
prev_handler, queue = mask_stack.pop()
# restore the original signal handler
signal.signal(signal.SIGINT, prev_handler)
# the remaining part may never occur if a signal happens right now
# but that's fine
for event in queue:
event()
mask()
with acquire_resource(…) as handle:
unmask()
…
如果SIGINT是我们关心的唯一来源,这将有效。不幸的是,它会因多个信号而崩溃,不仅因为我们不知道正在处理哪些信号,还因为我们不能原子地换掉多个信号!