-
Notifications
You must be signed in to change notification settings - Fork 768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More error handling #358
More error handling #358
Changes from all commits
fc421bd
5b9a44d
e26257b
0a59fa6
7bc66b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
import inspect | ||
|
||
from twisted.python import log | ||
from twisted.python.failure import Failure | ||
from twisted.application import service | ||
from twisted.internet.defer import Deferred, \ | ||
maybeDeferred, \ | ||
|
@@ -84,7 +85,29 @@ def _reject_future(future, error): | |
|
||
@staticmethod | ||
def _add_future_callbacks(future, callback, errback): | ||
return future.addCallbacks(callback, errback) | ||
# callback and/or errback may be None | ||
|
||
# Note that errback takes 3 args: type, exception, tb our | ||
# errbacks can be consistent between Twisted and asyncio. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, both should use 1 argument: the error that happened. is this not the case currently? I don't get it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, currently it's inconsistent: if you're using Twisted, you get a Failure and if you're using asyncio you get just the Exception instance. In this case, you can't print out a traceback (and your error-handler has to do isinstance() or some tricks to figure out if you're in asyncio or Twisted). My previous attempt would work in some cases (e.g. you can call sys.exc_info() but only if onUserError was getting called from inside an So, I switched them to the same args that sys.exc_info() returns so that they're a) consistent, and b) you can actually print out a traceback if you need/want to. We could switch to having a single arg (the exception instance, for example) but that's a lot less flexible, IMO (and would still need a wrapper-errback for Twisted to extract the exception from the Failure). (Also, maybe I'm missing something, but I thought those There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see. So that's the issue with the current code even: with Twisted, app code gets There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at the code in Twisted's Failure, for example, my opinion would be that we should provide at least the 3 args from sys.exc_info() -- otherwise, I don't know of any way that you could print out a stack trace in your error handler. All you'd be able to do is print out the Exception instance. As I noted, another alternative would be to provide our own Failure-type object for asyncio (and then in Twisted you could get your Failure and in asyncio you'd get "some object that has many of the same methods"). Not sure if this is a "win" or not though ;) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Providing 3 args: doesn't that mean us inventing even a 3rd way of "wrapping" the same information? The other being Twisted failure, and the missing asyncio way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All 3 of those bits of info are in the Failure, and we have to provde a Twisted errback-wrapper for either the 3-arg case or the "just the Exception" case anyway. For asyncio, we immediately have access to those args (we're in "except") and are directly calling the errback() anyway. So, it maps fairly well to how it was already working -- except that you'd get the same thing in the errback no matter if you're in Twisted or asyncio. The "other" option (our own Failure-type thing for asyncio) means no wrapper-errback for Twisted, but probably more work (although, it could just be a simple data-container for now that just has self.type, self.value and self.tb I guess). It would have the advange for Twisted users of being a more-"normal" single-arg errback too. What I mean is the code would look more like "normal" Twisted code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd probably lean towards the "Failure-ish thing for asyncio case", but I'm probably really biased since I have more Twisted than asyncio experience ;) |
||
|
||
# alternatively: what if we made a dummy Failure-like thing | ||
# for asyncio so our "standard" errback in protocol.py could | ||
# use the Failure API, or at least the bits we need...like | ||
# collecting the traceback frames. | ||
|
||
def _errback(fail): | ||
# converting to common API between asyncio/Twisted | ||
return errback(fail.type, fail.value, fail.tb) | ||
|
||
if callback is None: | ||
assert errback is not None | ||
future.addErrback(_errback) | ||
elif errback is None: | ||
future.addCallback(callback) | ||
else: | ||
future.addCallbacks(callback, _errback) | ||
|
||
return future | ||
|
||
@staticmethod | ||
def _gather_futures(futures, consume_exceptions=True): | ||
|
@@ -96,15 +119,14 @@ class ApplicationSession(FutureMixin, protocol.ApplicationSession): | |
WAMP application session for Twisted-based applications. | ||
""" | ||
|
||
def onUserError(self, e, msg): | ||
def onUserError(self, typ, exc, tb, msg=None): | ||
""" | ||
Override of wamp.ApplicationSession | ||
""" | ||
# see docs; will print currently-active exception to the logs, | ||
# which is just what we want. | ||
log.err() | ||
# also log the framework-provided error-message | ||
log.err(msg) | ||
if msg: | ||
log.err(Failure(exc, typ, tb), msg) | ||
else: | ||
log.err(Failure(exc, typ, tb)) | ||
|
||
|
||
class ApplicationSessionFactory(FutureMixin, protocol.ApplicationSessionFactory): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -262,7 +262,10 @@ class ISession(object): | |
@abc.abstractmethod | ||
def onConnect(self): | ||
""" | ||
Callback fired when the transport this session will run over has been established. | ||
Callback fired when the transport this session will run over has | ||
been established. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we decide to do that (which we need to discuss first since it is API changing), then we should do that in a different commit. In any case, looking at the impacts takes some thinking and time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're talking about the ability for those ISession methods to be async? There are some instances in Crossbar that decorate at least onJoin with inlineCallbacks -- I didn't think I was changing the API, just documenting it. So for example a simple e.g. RouterWorkerSession.onJoin is async, as is NativeProcessSession.onJoin in crossbar. For each ISession method, I'd like to document explicitly which ones can/cannot be async, and can certainly remove any of the async-handling test-cases for ones which shouldn't be. It should be harmless to leave the handling always-async in protocol.py but that could be changed also. So:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that ability. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I believe so. All I'm really changing in this PR is how the error-cases are handled. Currently, even with the Now, we will always attach _swallow_error() which will at least log the error (and then ignore it). Of course, this would be a "last-chance" handler -- if user-code put their own error handling inside the onJoin or whatever, they would get called first. At least, that's the theory ;) I did add both "returns Deferred" and "simply throws" unit-tests for all the ISession handlers, and covered all the code-paths in protocol.py that lead to those handlers being called. So: I don't expect any user-visible changes from this PR, except that errors now get logged. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall I add a unit-test for "multiple onJoin calls" just to be explict that's supported (and works)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, splitting it up would be awesome in this case. The asynch callbacks probably right away - you've convinced me on that one already. Rgd. the error thing: I tend to agree about preferring Twisted style;) Probably we could use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another note: not sure, what do you think? Probably the mixin should be named "loop mixins" and be something to be used in various contexts, not just WAMP transport factories or something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I'll make a new pull-request with just the error-handling things (including async/sync case) but not changing the errback or onUserError API. Separately, I'll play with the errback API and see if I can make something that follows Twisted style (with the real Failures and no errback wrapper) but that also provides a usable API for asyncio case as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. awesome! sounds great. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, while I'm thinking of it: do you prefer allowing For what it's worth, Twisted's |
||
May return a Deferred/Future. | ||
""" | ||
|
||
@abc.abstractmethod | ||
|
@@ -276,6 +279,8 @@ def onChallenge(self, challenge): | |
""" | ||
Callback fired when the peer demands authentication. | ||
|
||
May return a Deferred/Future. | ||
|
||
:param challenge: The authentication challenge. | ||
:type challenge: Instance of :class:`autobahn.wamp.types.Challenge`. | ||
""" | ||
|
@@ -285,6 +290,8 @@ def onJoin(self, details): | |
""" | ||
Callback fired when WAMP session has been established. | ||
|
||
May return a Deferred/Future. | ||
|
||
:param details: Session information. | ||
:type details: Instance of :class:`autobahn.wamp.types.SessionDetails`. | ||
""" | ||
|
@@ -306,6 +313,8 @@ def onLeave(self, details): | |
""" | ||
Callback fired when WAMP session has is closed | ||
|
||
May return a Deferred/Future. | ||
|
||
:param details: Close information. | ||
:type details: Instance of :class:`autobahn.wamp.types.CloseDetails`. | ||
""" | ||
|
@@ -320,6 +329,9 @@ def disconnect(self): | |
def onDisconnect(self): | ||
""" | ||
Callback fired when underlying transport has been closed. | ||
|
||
May return a Deferred/Future (but note that the underlying | ||
transport is already gone). | ||
""" | ||
|
||
@abc.abstractmethod | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as said, I won't merge this