Twisted Deferred 在没有 errback 的情况下不显示未处理的异常

Twisted Deferred not displaying unhandled Exception without errback

我正在阅读 McKellar 和 Fettig 的 Twisted Network Programming Essentials, 2nd Ed.

我在 运行宁 Twisted 15.5.0 Python 2.7.10 Windows 7.

在关于 Deferred 的部分中,有一个示例应该引发 Unhandled Error in Deferred - 但是当我 运行 下面的最小示例时,我只是从控制台获得完全的静音:

最小示例

from twisted.internet.defer import Deferred

def raiseErr(err):
    raise Exception(err)

d = Deferred()
d.addCallback(raiseErr)
d.callback("oh no")

$ python test.py (no output)

实际书籍文本中的最小示例

书中的实际例子是这样的:

from twisted.internet.defer import Deferred

def callback1(result):
    print "Callback 1 said:", result
    return result

def callback2(result):
    print "Callback 2 said:", result

def callback3(result):
    raise Exception("Callback 3")

def errback1(failure):
    print "Errback 1 had an an error on", failure
    return failure

d = Deferred()
d.addCallback(callback1)
d.addCallback(callback2)
d.addCallback(callback3)
d.callback("Test")

预期输出在书中列为:

callback3 raises an Exception, and because there is no registered errback to handle the Exception, the program terminates and reports an Unhandled Error to the user. The result is:

Callback 1 said: Test
Callback 2 said: Test
Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
File "/tmp/test.py", line 33, in <module>
d.callback("Test")
<...>
File "/tmp/test.py", line 11, in callback3
raise Exception("Callback 3")
exceptions.Exception: Callback 3

我是不是做错了什么?

编辑:

我遇到了在我的机器上正确显示的错误。

为了在 Deferred object 上没有 errback 处理程序的情况下记录错误,我需要将以下内容添加到我的代码片段中:

import sys
from twisted.python import log

log.startLogging(sys.stdout)

# rest of the code goes here

现在,当我 运行 来自问题中第一个代码片段的最小示例时,我得到以下输出:

2016-02-05 09:45:43-0600 [-] Log opened.
2016-02-05 09:45:43-0600 [-] Invalid format string or unformattable object in log message: '%(log_legacy)s', {'format': '%(log_legacy)s', 'log_legacy': <twisted.logger._stdlib.StringifiableFromEvent object at 0x038913F0>, 'time': 1454687143.778, 'message': (), 'log_time': 1454687143.778, 'log_namespace': 'twisted.internet.defer', 'log_level': <LogLevel=critical>, 'log_source': None, 'system': '-', 'isError': True, 'log_logger': <Logger 'twisted.internet.defer'>, 'log_format': 'Unhandled error in Deferred:'}
2016-02-05 09:45:43-0600 [-] Unhandled Error
        Traceback (most recent call last):
          File "testd.py", line 13, in <module>
            d.callback("oh no")
          File "C:\Swat\.virtualenvs\twisted\lib\site-packages\twisted\internet\defer.py", line 393, in callback
            self._startRunCallbacks(result)
          File "C:\Swat\.virtualenvs\twisted\lib\site-packages\twisted\internet\defer.py", line 501, in _startRunCallbacks
            self._runCallbacks()
        --- <exception caught here> ---
          File "C:\Swat\.virtualenvs\twisted\lib\site-packages\twisted\internet\defer.py", line 588, in _runCallbacks
            current.result = callback(current.result, *args, **kw)
          File "testd.py", line 9, in raiseErr
            raise Exception(err)
        exceptions.Exception: oh no

所以,现在我可以验证 Twisted 确实会按预期引发错误 - 只是出于某种原因不想告诉我。如果有人可以详细说明为什么在没有定义 errback 的情况下处理异常的默认情况,我很想知道。

我更改了标题以反映我的新问题。

我认为我们的代码没有任何问题。

我在这里试过运行:

$ python deferred.py

输出为:

Unhandled error in Deferred:


Traceback (most recent call last):
  File "deferred.py", line 8, in <module>
    d.callback("oh no")
  File "/home/vagrant/.virtualenvs/asyncproxy/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 393, in callback
    self._startRunCallbacks(result)
  File "/home/vagrant/.virtualenvs/asyncproxy/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 501, in _startRunCallbacks
    self._runCallbacks()
--- <exception caught here> ---
  File "/home/vagrant/.virtualenvs/asyncproxy/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 588, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "deferred.py", line 4, in raiseErr
    raise Exception(err)
exceptions.Exception: oh no

我正在使用 Twisted 15.5.0 和 Python 2.7.6.

P.S: 没有必要初始化日志。没有它也能工作。

此代码在我测试过的所有 Windows 机器和 Linux 机器的 none 上给出了相同的结果。我得出的结论是,这是与 Windows、twisted 或 pywin32 有关的问题。

我在研究 Dave Perticola 的优秀文章 Twisted Introduction specifically in Part 9: A Second Interlude, Deferred he has an example 时遇到了一个非常相似的问题后发现了这个问题,与您发布的问题相似:

from twisted.internet.defer import Deferred
def callback(res):
    raise Exception('oops')
d = Deferred()
d.addCallback(callback)
d.callback('Here is your result.')
print "Finished"

就像你一样,我没有得到建议的输出:

Finished
Unhandled error in Deferred:
Traceback (most recent call last):
 ...
--- <exception caught here> ---
 ...
exceptions.Exception: oops

我最初认为是版本控制问题,对我来说是在 EL6 上使用 PyPy 5.0.1 的 Twisted 16,但研究将我带到了这里;然后按照另一个答案中的建议打开日志记录时,对我认为的实际答案没有影响。

我的答案的线索是在我正在使用的教程中,声明如下:

the reason that “Finished” comes first is because the “Unhandled” message isn’t actually printed until the deferred is garbage collected.

出于某种原因,对于你我来说,当垃圾收集器开始做它的事情时,它已经没有地方可以输出了。如果你想看看这是真的,这是我修改后的代码:

import gc
from twisted.internet.defer import Deferred
def callback(res):
    raise Exception('oops call')
d = Deferred()
d.addCallback(callback)
d.callback('Here is your result.')
d = None
gc.collect()
print "Finished"

我想真正的答案是在垃圾收集器到达之前处理您的异常,但也许这些信息会让您睡得更轻松,即使我们不知道为什么它对某些人有用,我们知道是什么导致我们预期的错误消息消失。