如何向deferLater添加errback?

时间:2014-04-13 05:25:57

标签: python twisted

使用deferLater考虑以下扭曲代码:

import random

from twisted.internet.task import deferLater
from twisted.internet import reactor

def random_exception(msg='general'):
    if random.random() < 0.5:
        raise Exception("Random exception with 50%% likelihood occurred in %s!" % msg)

def dolater():
    random_exception('dolater')
    print "it's later!"

def whoops(failure):
    failure.trap(Exception)
    print failure

defer = deferLater(reactor, 10, dolater)
defer.addErrback(whoops)

reactor.run()

在10秒睡眠期间引发异常(即 KeyboardInterrupt ),但似乎永远不会调用whoops方法。我的假设是,因为我在延迟启动后添加了errBack,所以它从未正确注册。建议表示赞赏。

修改

好吧,没有人喜欢我使用信号(不是例外)KeyboardInterrupt来显示延迟之外的错误情况。我对于延迟回调可能发生的实际异常非常认真,但无法想到一个特别好的异常,大多数都是某种信号(或开发人员错误),所以信号处理很好现在 - 但这不是问题的核心。

据我所知,twisted的回调/错误回调系统处理回调结构中的错误 - 例如如果dolater提出某种异常。为了说明这一点,我添加了一个在dolater期间可能发生的异常,以表明如果在dolater中发生异常,则errback处理异常就好了。

我担心的是,当反应堆正常反应时出现问题,我唯一能够出错的是键盘中断,然后我想要呐喊。似乎如果我将其他异步事件放入反应器并从那里引发异常,那么dolater代码就不会受到影响,我将不得不向其他异步事件添加错误。整个扭曲程序没有主错误处理。

所以发出信号,直到我找到某种方法使反应堆在没有信号的情况下失败。

3 个答案:

答案 0 :(得分:0)

问题在于您尝试捕获的实际异常,特别是KeyboardInterrupt不是Exception的子类,因此无法捕获它。如果您只是更改一行:

failure.trap(Exception)

成:

failure.trap(KeyboardInterrupt)
肯定会抓住它。有关Python的异常层次结构的更多信息可以在官方Python文档中找到:https://docs.python.org/2/library/exceptions.html

答案 1 :(得分:0)

Twisted是一个同时执行许多操作的库。事情尽可能保持孤立(假设这仍然是Python,还有全局状态等)。

如果你有一个有两个客户端的TCP服务器连接到它,其中一个发送给你一些坏的数据,这些数据会导致你的解析器中出现导致异常被引发的错误,那个异常不会导致其他客户端收到任何错误。我希望(至少不会自动),你也不希望它。

同样,如果您的客户端已连接到您的服务器并且您使用deferLater启动了延迟调用,并且客户端触发了该错误,则您不希望将错误传递给{{{ 1 {}由Deferred返回。

这里的想法是,通常单独处理单独的事件源(直到编写一些代码将它们粘合在一起)。

当您致电deferLater和Twisted开始运行您传递给deferLater的函数之间经过的十秒钟时,发生任何错误 - 包括您在键盘上点击Cc Python引发了一个KeyboardInterrupt - 与该延迟调用没有关联,并且它们不会被传递给你附加到deferLater的错误。

只有Deferred函数引发的异常才会导致dolater的错误链开始执行。

答案 2 :(得分:0)

如果KeyboardInterrupt表示信号(ctrl-cSIGINT等),那么您需要设置一个信号处理程序,并将whoops函数设置为回调。

通过以下两个来自@ jean-paul-calderone twisted: catch keyboardinterrupt and shutdown properlytwisted - interrupt callback via KeyboardInterrupt的答案,我尝试了以下内容,我认为它符合您的需求:

def dolater():
    print "it's later!"

def whoops(signal, stackframe):
    print "I'm here because of signal number " + str(signal)
    reactor.stop()

defer = task.deferLater(reactor, 10, dolater)
signal.signal(signal.SIGINT, whoops)
reactor.run()

这会在SIGINT上调用哎呀。我把一个reactor.stop()放在whoops中,因为否则反应器会继续运行,如果你真的希望它继续在ctrl-c面前运行,那就把它取出来。

注意:我没有明确说明如何解决信号系统中的错误问题,因为(至少在我的理解下)并没有真正映射到应该如何使用延迟信号。我想如果你找到了一种方法来推迟信号处理程序,你可以fire它的错误,但我认为这是因为扭曲的预期用例,可能会产生疯狂的后果。