def _callRemote(self, _name, *args, **kwargs): req = None broker = self.tracker.broker methodConstraintOverride = kwargs.get("_methodConstraint", STUB) resultConstraint = kwargs.get("_resultConstraint", STUB) useSchema = kwargs.get("_useSchema", True) callOnly = kwargs.get("_callOnly", False) if "_methodConstraint" in kwargs: del kwargs["_methodConstraint"] if "_resultConstraint" in kwargs: del kwargs["_resultConstraint"] if "_useSchema" in kwargs: del kwargs["_useSchema"] if "_callOnly" in kwargs: del kwargs["_callOnly"] if callOnly: if broker.disconnected: # DeadReferenceError is silently consumed return reqID = 0 else: # newRequestID() could fail with a DeadReferenceError reqID = broker.newRequestID() # in this section, we validate the outbound arguments against our # notion of what the other end will accept (the RemoteInterface) # first, figure out which method they want to invoke (interfaceName, methodName, methodSchema) = self._getMethodInfo(_name) req = call.PendingRequest(reqID, self, interfaceName, methodName) # TODO: consider adding a stringified stack trace to that # PendingRequest creation, so that DeadReferenceError can emit even # more information about the call which failed # for debugging: these are put into the messages emitted when # logRemoteFailures is turned on req.interfaceName = interfaceName req.methodName = methodName if methodConstraintOverride is not STUB: methodSchema = methodConstraintOverride if useSchema and methodSchema: # check args against the arg constraint. This could fail if # any arguments are of the wrong type try: methodSchema.checkAllArgs(args, kwargs, False) except Violation as v: v.setLocation("%s.%s(%s)" % (interfaceName, methodName, v.getLocation())) raise # the Interface gets to constraint the return value too, so # make a note of it to use later req.setConstraint(methodSchema.getResponseConstraint()) # if the caller specified a _resultConstraint, that overrides # the schema's one if resultConstraint is not STUB: # overrides schema req.setConstraint(IConstraint(resultConstraint)) clid = self.tracker.clid slicer = call.CallSlicer(reqID, clid, methodName, args, kwargs) # up to this point, we are not committed to sending anything to the # far end. The various phases of commitment are: # 1: once we tell our broker about the PendingRequest, we must # promise to retire it eventually. Specifically, if we encounter an # error before we give responsibility to the connection, we must # retire it ourselves. # 2: once we start sending the CallSlicer to the other end (in # particular, once they receive the reqID), they might send us a # response, so we must be prepared to handle that. Giving the # PendingRequest to the broker arranges for this to happen. # So all failures which occur before these commitment events are # entirely local: stale broker, bad method name, bad arguments. If # anything raises an exception before this point, the PendingRequest # is abandoned, and our maybeDeferred wrapper returns a failing # Deferred. # commitment point 1. We assume that if this call raises an # exception, the broker will be sure to not track the dead # PendingRequest if not callOnly: broker.addRequest(req) # if callOnly, the PendingRequest will never know about the # broker, and will therefore never ask to be removed from it # TODO: there is a decidability problem here: if the reqID made # it through, the other end will send us an answer (possibly an # error if the remaining slices were aborted). If not, we will # not get an answer. To decide whether we should remove our # broker.waitingForAnswers[] entry, we need to know how far the # slicing process made it. try: # commitment point 2 d = broker.send(slicer) # d will fire when the last argument has been serialized. It will # errback if the arguments (or any of their children) could not # be serialized. We need to catch this case and errback the # caller. # if we got here, we have been able to start serializing the # arguments. If serialization fails, the PendingRequest needs to # be flunked (because we aren't guaranteed that the far end will # do it). d.addErrback(req.fail) except Exception: req.fail(failure.Failure()) # @todo: (!?) timeout """ [bw] debug def cb(result, *args): print('-cb-', args, '-->', result) return result print('--', reqID, interfaceName, methodName) d.addBoth(cb, 'broker.send', slicer) req.deferred.addBoth(cb, 'call.PendingRequest', reqID) import twisted.internet.reactor as reactor reactor.callLater(2.0, req.deferred.cancel) """ # the remote end could send back an error response for many reasons: # bad method name # bad argument types (violated their schema) # exception during method execution # method result violated the results schema # something else could occur to cause an errback: # connection lost before response completely received # exception during deserialization of the response # [but only if it occurs after the reqID is received] # method result violated our results schema # if none of those occurred, the callback will be run return req.deferred
def _callRemote(self, _name, *args, **kwargs): req = None broker = self.tracker.broker # remember that "none" is not a valid constraint, so we use it to # mean "not set by the caller", which means we fall back to whatever # the RemoteInterface says. Using None would mean an AnyConstraint, # which is not the same thing. methodConstraintOverride = kwargs.get("_methodConstraint", "none") resultConstraint = kwargs.get("_resultConstraint", "none") useSchema = kwargs.get("_useSchema", True) callOnly = kwargs.get("_callOnly", False) if "_methodConstraint" in kwargs: del kwargs["_methodConstraint"] if "_resultConstraint" in kwargs: del kwargs["_resultConstraint"] if "_useSchema" in kwargs: del kwargs["_useSchema"] if "_callOnly" in kwargs: del kwargs["_callOnly"] if callOnly: if broker.disconnected: # DeadReferenceError is silently consumed return reqID = 0 else: # newRequestID() could fail with a DeadReferenceError reqID = broker.newRequestID() # in this section, we validate the outbound arguments against our # notion of what the other end will accept (the RemoteInterface) # first, figure out which method they want to invoke (interfaceName, methodName, methodSchema) = self._getMethodInfo(_name) req = call.PendingRequest(reqID, self, interfaceName, methodName) # TODO: consider adding a stringified stack trace to that # PendingRequest creation, so that DeadReferenceError can emit even # more information about the call which failed # for debugging: these are put into the messages emitted when # logRemoteFailures is turned on req.interfaceName = interfaceName req.methodName = methodName if methodConstraintOverride != "none": methodSchema = methodConstraintOverride if useSchema and methodSchema: # check args against the arg constraint. This could fail if # any arguments are of the wrong type try: methodSchema.checkAllArgs(args, kwargs, False) except Violation, v: v.setLocation("%s.%s(%s)" % (interfaceName, methodName, v.getLocation())) raise # the Interface gets to constraint the return value too, so # make a note of it to use later req.setConstraint(methodSchema.getResponseConstraint())