Deferred.callback()或Deferred.errback()能否向调用者抛出异常?

1 投票
1 回答
603 浏览
提问于 2025-04-17 21:48

有没有可能在调用 .callback().errback() 时,出现一种情况,让调用者收到异常,而这个异常不会被 deferred 捕获呢?

假设我有以下的 deferred 和回调:

from twisted.internet import defer

def bad_callback(result):
    raise Exception()

def bad_errback(result):
    raise Exception()

d = defer.Deferred()
d.addCallbacks(bad_callback, bad_errback)

如果我调用 d.callback(None),那么 d 的结果将是来自 bad_callback()Exception。如果我调用 d.errback(Exception()),那么 d 的结果将是来自 bad_errback()Exception。但是,在这两种情况下,异常都不会抛给调用者。

我确实知道几种情况,在这些情况下调用 .callback().errback() 会抛出异常给调用者,但这些情况都是不正确使用 deferred 的情况。

  • 显然,如果你用不正确的参数数量调用 .callback().errback(),会抛出 TypeError

  • 如果你调用一个已经被调用过的 deferred,会抛出 AlreadyCalledError

  • 调用 .callback(defer.Deferred()) 会抛出 AssertionError

  • 调用 .errback() 相当于调用 .errback(failure.Failure()),如果没有活动的异常,会抛出 NoCurrentExceptionError

我真正想问的是:我能否安全地依赖于这样的行为:只要 deferred 还没有被调用,并且结果是正确的,调用 .callback(result).errback(exception_or_failure) 不会抛出异常?

1 个回答

2

我运行了你的例子,并在底部加了两行:

d.callback(None)
print("OK!")

然后得到了这个输出:

Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
  File "callbacks.py", line 11, in <module>
    d.callback(None)
  File ".../twisted/internet/defer.py", line 368, in callback
    self._startRunCallbacks(result)
  File ".../twisted/internet/defer.py", line 464, in _startRunCallbacks
    self._runCallbacks()
--- <exception caught here> ---
  File ".../twisted/internet/defer.py", line 551, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "callbacks.py", line 4, in bad_callback
    raise Exception()
exceptions.Exception: 
OK!

所以在这个特定的情况下(正如你自己所确定的),不,异常不会被重新抛出。

一般来说,有几个地方会让异常“有效地”传播出去;比如说,如果你遇到 MemoryError,也就是内存完全用完了,可能 Deferred 的实现会尝试调用某个函数来分配一点内存,这样异常就会返回给你。

不过,这只是使用 Python 编程时的一种风险;有几种异常(比如 MemoryErrorKeyboardInterrupt)可能会在没有任何警告的情况下出现。如果你的整个程序没有崩溃,那么不,callbackerrback 不会抛出异常,除非是在你提到的那些情况下。

撰写回答