【问题标题】:Errback not being fired on unhandled exception within @inlineCallbacks-decorated function在@inlineCallbacks-decorated 函数中未处理未处理的异常时不会触发 Errback
【发布时间】:2023-12-06 18:15:01
【问题描述】:
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks


def fail():
  raise Exception()

@inlineCallbacks
def foo():
  yield reactor.callLater(5.0, fail)

def dump(*args, **kwargs):
  print 'dump', args, kwargs

d = foo()
d.addErrback(dump)

reactor.run()

这里我们有一个虚拟函数foo(),它产生一个将在 5 秒内触发的 Deferred。当它触发时,会抛出一个异常,我希望它会被与我的* Deferred 对象关联的 errback 捕获:

  1. foo() 被调用并立即返回一个 Deferred。我们向它添加一个 errback,它只是打印出它的参数。
  2. 几秒钟后,反应器调用fail()
  3. fail() 抛出异常。
  4. 异常被“抛出”到生成器的产生点。 Documentation:

生成器将通过生成器上的“发送”方法发送延迟的结果,或者如果结果失败,则“抛出”。

  1. 异常未在生成器内部捕获,因此 foo() 的 Deferred 应调用其 errback:

启用 inlineCallbacks 的生成器将返回一个 Deferred 对象,如果您的生成器引发未处理的异常,该对象将失败并返回一个失败对象

  1. 在调用 errback 时,应调用 dump()

改为:

Unhandled Error
Traceback (most recent call last):
  File "untitled", line 19, in <module>
    reactor.run()
  File "/Library/Python/2.7/site-packages/twisted/internet/base.py", line 1192, in run
    self.mainLoop()
  File "/Library/Python/2.7/site-packages/twisted/internet/base.py", line 1201, in mainLoop
    self.runUntilCurrent()
--- <exception caught here> ---
  File "/Library/Python/2.7/site-packages/twisted/internet/base.py", line 824, in runUntilCurrent
    call.func(*call.args, **call.kw)
  File "untitled", line 6, in fail
    raise Exception()
exceptions.Exception:

为了进一步测试这一点,我尝试 (a) 直接在 foo() 内引发异常,以及 (b) 尝试在 foo() 内捕获 fail() 引发的异常。

(a) 工作正常,我的 errback 被调用。

(b) 不起作用,并导致相同的问题:

@inlineCallbacks
def foo():
  try:
    yield reactor.callLater(5.0, fail)
  except Exception, e:
    print e 

这是 Twisted 15.1.0 和 Python 2.7.10。

【问题讨论】:

    标签: python twisted


    【解决方案1】:

    您也可以改用deferLater,因为这将返回您想要的Deferred

    from twisted.internet import task, defer, reactor
    
    def fail(*args):
        raise Exception()
    
    def error(*args):
        print(args)
    
    def dump(*args, **kwargs):
        print('dump', args, kwargs)
    
    @defer.inlineCallbacks
    def foo():
        d = task.deferLater(reactor, 1.0, fail)
        d.addErrback(error)
        yield d
    
    D = foo()
    D.addCallback(dump)
    reactor.run()
    

    【讨论】:

      【解决方案2】:

      啊,reactor.callLater 没有返回 Deferred。您可以使用它

      def fail(_):
        raise Exception()
      
      @inlineCallbacks
      def foo():
        d = Deferred()
        d.addCallback(fail)
        reactor.callLater(1.0, d.callback, None)
        yield d
      

      【讨论】:

        最近更新 更多