Ответ 1
Позвольте немного изучить "усвоить". Что значит "усвоить" исключение?
Здесь самая прямая и, я думаю, верная интерпретация:
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
". Поскольку вы упомянули об ошибках, я предполагаю, что это тот случай, когда вы кусаете вас.
Deferred
может иметь успех или результат неудачи. Когда он имеет какой-либо результат во всех и более обратных вызовах или ошибках, он попытается передать результат либо на следующий обратный вызов, либо на возврат. Результат Deferred
затем становится результатом вызова одной из этих функций. Как только Deferred
ушел, хотя все его обратные вызовы и errbacks, он держится за свой результат, если к нему добавятся дополнительные обратные вызовы или errback.
Если " Deferred
заканчивается результатом отказа и не имеет никаких ошибок, то она просто сидит на этом сбое. Если он получает мусор, собранный до ошибки, которая обрабатывает этот отказ, добавляется к нему, тогда он будет регистрировать исключение. Вот почему у вас всегда должны быть ошибки на вашем Отсрочке, по крайней мере, чтобы вы могли своевременно регистрировать неожиданные исключения (а не быть подчиненными прихотям сборщика мусора).
Если мы перейдем к предыдущему примеру и рассмотрим поведение, когда сервер не прослушивает 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)
Тогда выход несколько более информативен:
[email protected]:/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
, ошибки, из которых действительно являются фатальными, и должны остановить ваше приложение, тогда вы можете определить подходящую ошибку и приложить ее к этому Deferred
:
d = e.connect(f)
def fatalError(reason):
err(reason, "Absolutely needed the foo, could not get it")
reactor.stop()
d.addErrback(fatalError)