gpt4 book ai didi

python - 停止 Twisted 吞下异常

转载 作者:IT老高 更新时间:2023-10-28 22:22:37 26 4
gpt4 key购买 nike

有没有办法阻止 Twisted reactor 自动吞下异常(例如 NameError)?我只是想让它停止执行,并在控制台中给我一个堆栈跟踪?

甚至还有常见问题解答 question关于它,但至少可以说,它不是很有帮助。

目前,在每个错误返回中,我都这样做:

def errback(value):
import traceback
trace = traceback.format_exc()
# rest of the errback...

但这感觉很笨重,必须有更好的方法吗?

更新

为了回应让-保罗的回答,我尝试运行以下代码(使用 Twisted 11.1 和 12.0):
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet import protocol, reactor

class Broken(protocol.Protocol):
def connectionMade(self):
buggy_user_code()

e = TCP4ClientEndpoint(reactor, "127.0.0.1", 22)
f = protocol.Factory()
f.protocol = Broken
e.connect(f)
reactor.run()

运行它后,它只是卡在那里,所以我必须 Ctrl-C 它:
> python2.7 tx-example.py
^CUnhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
Failure: twisted.internet.error.ConnectionRefusedError: Connection was refused by other side: 111: Connection refused.

最佳答案

让我们稍微探索一下“吞”。 “吞下”异常是什么意思?

这是我认为最直接、最忠实的解释:

try:
user_code()
except:
pass

在这里,任何来自调用用户代码的异常都会被捕获,然后被丢弃而不采取任何行动。如果您浏览 Twisted,我认为您不会在任何地方找到这种模式。如果你这样做,这是一个可怕的错误和错误,你会通过提交一个错误来帮助项目指出它。

还有什么可能导致“吞咽异常”?一种可能性是异常来自根本不应该引发异常的应用程序代码。这通常在 Twisted 中通过记录异常然后继续进行处理,可能是在将应用程序代码与它所连接的任何事件源断开连接之后。考虑这个有问题的应用程序:
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet import protocol, reactor

class Broken(protocol.Protocol):
def connectionMade(self):
buggy_user_code()


e = TCP4ClientEndpoint(reactor, "127.0.0.1", 22)
f = protocol.Factory()
f.protocol = Broken
e.connect(f)
reactor.run()

运行时(如果您的服务器在 localhost:22 上运行,因此连接成功并且 connectionMade 实际上被调用),产生的输出是:
Unhandled Error
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 84, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 69, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
return func(*args,**kw)
--- <exception caught here> ---
File "/usr/lib/python2.7/dist-packages/twisted/internet/selectreactor.py", line 146, in _doReadOrWrite
why = getattr(selectable, method)()
File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 674, in doConnect
self._connectDone()
File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 681, in _connectDone
self.protocol.makeConnection(self)
File "/usr/lib/python2.7/dist-packages/twisted/internet/protocol.py", line 461, in makeConnection
self.connectionMade()
File "/usr/lib/python2.7/dist-packages/twisted/internet/endpoints.py", line 64, in connectionMade
self._wrappedProtocol.makeConnection(self.transport)
File "/usr/lib/python2.7/dist-packages/twisted/internet/protocol.py", line 461, in makeConnection
self.connectionMade()
File "proderr.py", line 6, in connectionMade
buggy_user_code()
exceptions.NameError: global name 'buggy_user_code' is not defined

这个错误显然没有被吞下。即使此应用程序尚未以任何特定方式初始化日志记录系统,记录的错误仍会显示。如果日志系统的初始化方式会导致错误转移到其他地方——比如某个日志文件或/dev/null——那么错误可能不会那么明显。不过,您必须不遗余力地导致这种情况发生,并且大概如果您将日志系统定向到/dev/null ,那么如果您没有看到任何记录的错误,您就不会感到惊讶。

一般来说,在 Twisted 中没有办法改变这种行为。每个异常处理程序都是在调用应用程序代码的调用站点单独实现的,并且每个异常处理程序都单独实现以执行相同的操作 - 记录错误。

另一个值得研究的案例是异常如何与 Deferred 交互。类(class)。既然你提到了 错误回复 我猜这就是咬你的情况。

一个 Deferred可以有一个成功的结果或一个失败的结果。当它有任何结果和更多的回调或 errback 时,它会尝试将结果传递给下一个回调或 errback。 Deferred的结果然后成为调用这些函数之一的结果。尽快 Deferred已经完成了所有的回调和 errback,它会保留它的结果,以防更多的回调或 errback 添加到它。

如果 Deferred最终得到一个失败结果并且没有更多的错误返回,那么它只是坐在那个失败上。如果它在处理该故障的 errback 添加到它之前被垃圾收集,那么它将记录异常。这就是为什么你应该总是在你的延迟上有错误回复,至少这样你可以及时记录意外的异常(而不是受到垃圾收集器的奇思妙想)。

如果我们重新访问前面的示例并考虑没有服务器监听 localhost:22 时的行为(或将示例更改为连接到没有服务器监听的其他地址),那么我们得到的恰好是 Deferred失败的结果并且没有错误返回来处理它。
e.connect(f)

此调用返回 Deferred ,但调用代码只是丢弃它。因此,它没有回调或错误返回。当它得到它的失败结果时,没有代码来处理它。该错误仅在 Deferred 时记录是垃圾收集,它发生在不可预测的时间。通常,特别是对于非常简单的示例,直到您尝试关闭程序(例如通过 Control-C)才会进行垃圾收集。结果是这样的:
$ python someprog.py
... wait ...
... wait ...
... wait ...
<Control C>
Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
Failure: twisted.internet.error.ConnectionRefusedError: Connection was refused by other side: 111: Connection refused.

如果你不小心写了一个大程序并在某处落入了这个陷阱,但你不确定在哪里,那么 twisted.internet.defer.setDebugging可能会有所帮助。如果示例更改为使用它来启用 Deferred调试:
from twisted.internet.defer import setDebugging
setDebugging(True)

然后输出会提供更多信息:
exarkun@top:/tmp$ python proderr.py
... wait ...
... wait ...
... wait ...
<Control C>
Unhandled error in Deferred:
(debug: C: Deferred was created:
C: File "proderr.py", line 15, in <module>
C: e.connect(f)
C: File "/usr/lib/python2.7/dist-packages/twisted/internet/endpoints.py", line 240, in connect
C: wf = _WrappingFactory(protocolFactory, _canceller)
C: File "/usr/lib/python2.7/dist-packages/twisted/internet/endpoints.py", line 121, in __init__
C: self._onConnection = defer.Deferred(canceller=canceller)
I: First Invoker was:
I: File "proderr.py", line 16, in <module>
I: reactor.run()
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/base.py", line 1162, in run
I: self.mainLoop()
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/base.py", line 1174, in mainLoop
I: self.doIteration(t)
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/selectreactor.py", line 140, in doSelect
I: _logrun(selectable, _drdw, selectable, method, dict)
I: File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 84, in callWithLogger
I: return callWithContext({"system": lp}, func, *args, **kw)
I: File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 69, in callWithContext
I: return context.call({ILogContext: newCtx}, func, *args, **kw)
I: File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
I: return self.currentContext().callWithContext(ctx, func, *args, **kw)
I: File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
I: return func(*args,**kw)
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/selectreactor.py", line 146, in _doReadOrWrite
I: why = getattr(selectable, method)()
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 638, in doConnect
I: self.failIfNotConnected(error.getConnectError((err, strerror(err))))
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 592, in failIfNotConnected
I: self.connector.connectionFailed(failure.Failure(err))
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/base.py", line 1048, in connectionFailed
I: self.factory.clientConnectionFailed(self, reason)
I: File "/usr/lib/python2.7/dist-packages/twisted/internet/endpoints.py", line 144, in clientConnectionFailed
I: self._onConnection.errback(reason)
)
Unhandled Error
Traceback (most recent call last):
Failure: twisted.internet.error.ConnectionRefusedError: Connection was refused by other side: 111: Connection refused.

注意顶部附近,其中 e.connect(f)线作为此 Deferred 的原点给出- 告诉您应该添加错误回复的可能位置。

但是,应该编写代码来向此 Deferred 添加错误回复。首先,至少要记录错误。

不过,有比您提供的方法更短(也更正确)的显示异常的方法。例如,考虑:
d = e.connect(f)
def errback(reason):
reason.printTraceback()
d.addErrback(errback)

或者,更简洁地说:
from twisted.python.log import err
d = e.connect(f)
d.addErrback(err, "Problem fetching the foo from the bar")

这种错误处理行为对于 Deferred 的想法有些基础。所以也不太可能改变。

如果您有 Deferred , 真正致命的错误必须停止您的应用程序,然后您可以定义一个合适的 errback 并将其附加到该 Deferred :
d = e.connect(f)
def fatalError(reason):
err(reason, "Absolutely needed the foo, could not get it")
reactor.stop()

d.addErrback(fatalError)

关于python - 停止 Twisted 吞下异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9295359/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com