有没有办法阻止Twisted reactor自动吞咽异常(例如NameError)?我只是想让它停止执行,并在控制台中给我一个堆栈跟踪?
甚至有关于它的FAQ 问题,但至少可以说,它不是很有用.
目前,在每个错误中我都这样做:
def errback(value):
import traceback
trace = traceback.format_exc()
# rest of the errback...
Run Code Online (Sandbox Code Playgroud)
但这感觉很笨重,还有更好的方法吗?
为了回应Jean-Paul的回答,我尝试运行以下代码(使用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()
Run Code Online (Sandbox Code Playgroud)
运行后,它只是挂在那里,所以我必须按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.
Run Code Online (Sandbox Code Playgroud)
Jea*_*one 22
让我们来探索一下"吞咽"吧."吞下"异常是什么意思?
这是最直接的,我认为,忠实的解释:
try:
user_code()
except:
pass
Run Code Online (Sandbox Code Playgroud)
此处捕获用户代码调用的任何异常,然后在不执行任何操作的情况下将其丢弃.如果你看一下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()
Run Code Online (Sandbox Code Playgroud)
运行时(如果服务器在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
Run Code Online (Sandbox Code Playgroud)
这个错误显然不会被吞噬.即使此应用程序尚未以任何特定方式初始化日志记录系统,仍会显示记录的错误.如果日志记录系统已经以导致错误的方式进行初始化 - 例如某些日志文件或/ dev/null - 那么错误可能不那么明显.你不得不尽力避免导致这种情况发生,并且假如你将日志记录系统指向/ dev/null,那么如果你没有看到任何记录的错误,你就不会感到惊讶.
通常,无法在Twisted中更改此行为.每个异常处理程序在调用应用程序代码的调用站点单独实现,并且每个异常处理程序单独实现以执行相同的操作 - 记录错误.
值得检查的另一个案例是异常如何与Deferred班级互动.既然你提到了errbacks,我猜这就是咬你的情况.
A Deferred可以有成功结果或失败结果.当它有任何结果以及更多的回调或错误时,它会尝试将结果传递给下一个回调或错误回复.的结果Deferred便成为调用的那些功能中的一个的结果.一旦完成Deferred了所有的回调和错误,它就会保留其结果,以防更多的回调或错误被添加到它.
如果Deferred最终导致失败结果并且没有更多的错误,那么它就会失败.如果在处理失败的errback之前收集了垃圾,那么它将记录异常.这就是为什么你应该总是在你的Deferreds上有错误的原因,至少这样你就可以及时记录意外的异常(而不是受垃圾收集器的影响).
如果我们重新访问上一个示例并考虑当没有服务器侦听localhost 时的行为:22(或者更改示例以连接到不同服务器正在侦听的其他地址),那么我们获得的是Deferred一个失败的结果没有错误处理它.
e.connect(f)
Run Code Online (Sandbox Code Playgroud)
此调用返回a 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.
Run Code Online (Sandbox Code Playgroud)
如果你不小心写了一个大型程序并陷入某个地方的陷阱,但你不确定在哪里,那么twisted.internet.defer.setDebugging可能会有所帮助.如果更改示例以使用它来启用Deferred调试:
from twisted.internet.defer import setDebugging
setDebugging(True)
Run Code Online (Sandbox Code Playgroud)
然后输出更有用:
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.
Run Code Online (Sandbox Code Playgroud)
注意在顶部附近,该e.connect(f)行作为其来源Deferred- 告诉您可能添加错误的地方.
但是,应该编写代码以首先添加Deferred错误,至少记录错误.
但是,显示异常的方式比您给出的方式更短(也更正确).例如,考虑:
d = e.connect(f)
def errback(reason):
reason.printTraceback()
d.addErrback(errback)
Run Code Online (Sandbox Code Playgroud)
或者,更简洁:
from twisted.python.log import err
d = e.connect(f)
d.addErrback(err, "Problem fetching the foo from the bar")
Run Code Online (Sandbox Code Playgroud)
这种错误处理行为对于这个想法来说有点基础Deferred,所以也不太可能改变.
如果你有一个Deferred,真正致命的错误并且必须停止你的应用程序,那么你可以定义一个合适的错误并将其附加到Deferred:
d = e.connect(f)
def fatalError(reason):
err(reason, "Absolutely needed the foo, could not get it")
reactor.stop()
d.addErrback(fatalError)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7439 次 |
| 最近记录: |