Example #1
0
    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
Example #2
0
    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())