Exemplo n.º 1
0
    def initFromMethod(self, method):
        # call this with the Interface's prototype method: the one that has
        # argument constraints expressed as default arguments, and which
        # does nothing but returns the appropriate return type

        names, _, _, typeList = inspect.getargspec(method)
        if names and names[0] == 'self':
            why = "RemoteInterface methods should not have 'self' in their argument list"
            raise InvalidRemoteInterface(why)
        if not names:
            typeList = []
        # 'def foo(oops)' results in typeList==None
        if typeList is None or len(names) != len(typeList):
            # TODO: relax this, use schema=Any for the args that don't have
            # default values. This would make:
            #  def foo(a, b=int): return None
            # equivalent to:
            #  def foo(a=Any, b=int): return None
            why = "RemoteInterface methods must have default values for all their arguments"
            raise InvalidRemoteInterface(why)
        self.argumentNames = names
        self.argConstraints = {}
        self.required = []
        for i in range(len(names)):
            argname = names[i]
            constraint = typeList[i]
            if not isinstance(constraint, Optional):
                self.required.append(argname)
            self.argConstraints[argname] = IConstraint(constraint)

        # call the method, its 'return' value is the return constraint
        self.responseConstraint = IConstraint(method())
        self.options = {}  # return, wait, reliable, etc
Exemplo n.º 2
0
    def __init__(self, method=None, _response=None, __options=[], **kwargs):
        if method:
            self.initFromMethod(method)
            return
        self.argumentNames = []
        self.argConstraints = {}
        self.required = []
        self.responseConstraint = None
        # __response in the argslist gets treated specially, I think it is
        # mangled into _RemoteMethodSchema__response or something. When I
        # change it to use _response instead, it works.
        if _response:
            self.responseConstraint = IConstraint(_response)
        self.options = {}  # return, wait, reliable, etc

        if "__ignoreUnknown__" in kwargs:
            self.ignoreUnknown = kwargs["__ignoreUnknown__"]
            del kwargs["__ignoreUnknown__"]
        if "__acceptUnknown__" in kwargs:
            self.acceptUnknown = kwargs["__acceptUnknown__"]
            del kwargs["__acceptUnknown__"]

        for argname, constraint in list(kwargs.items()):
            self.argumentNames.append(argname)
            constraint = IConstraint(constraint)
            self.argConstraints[argname] = constraint
            if not isinstance(constraint, Optional):
                self.required.append(argname)
Exemplo n.º 3
0
 def __init__(self, *attrTuples, **kwargs):
     self.ignoreUnknown = kwargs.get('ignoreUnknown', False)
     self.acceptUnknown = kwargs.get('acceptUnknown', False)
     self.keys = {}
     for name, constraint in (list(attrTuples) +
                              list(kwargs.get('attributes', {}).items())):
         assert name not in list(self.keys.keys())
         self.keys[name] = IConstraint(constraint)
Exemplo n.º 4
0
 def testAccept2(self):
     req = TestRequest(12)
     req.setConstraint(IConstraint(str))
     self.broker.addRequest(req)
     u = self.newUnslicer()
     u.start(0)
     u.checkToken(INT, 0)
     u.receiveChild(12)  # causes broker.getRequest
     u.checkToken(STRING, 15)
     u.receiveChild("results")
     self.assertFalse(req.answers)
     u.receiveClose()  # causes broker.gotAnswer
     self.assertEqual(req.answers, [(True, "results")])
Exemplo n.º 5
0
 def testReject2(self):
     # answer a request with a result that violates the constraint
     req = TestRequest(12)
     req.setConstraint(IConstraint(int))
     self.broker.addRequest(req)
     u = self.newUnslicer()
     u.checkToken(INT, 0)
     u.receiveChild(12)
     self.assertRaises(Violation, u.checkToken, STRING, 42)
     # this does not yet errback the request
     self.assertFalse(req.answers)
     # it gets errbacked when banana reports the violation
     v = Violation("icky")
     v.setLocation("here")
     u.reportViolation(BananaFailure(v))
     self.assertEqual(len(req.answers), 1)
     err = req.answers[0]
     self.assertFalse(err[0])
     f = err[1]
     self.assertTrue(f.check(Violation))
Exemplo n.º 6
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
Exemplo n.º 7
0
 def __init__(self, *alternatives):
     self.alternatives = [IConstraint(a) for a in alternatives]
     self.alternatives = tuple(self.alternatives)
Exemplo n.º 8
0
 def __init__(self, *elemConstraints):
     self.constraints = [IConstraint(e) for e in elemConstraints]
Exemplo n.º 9
0
 def __init__(self, keyConstraint, valueConstraint, maxKeys=None):
     self.keyConstraint = IConstraint(keyConstraint)
     self.valueConstraint = IConstraint(valueConstraint)
     self.maxKeys = maxKeys
Exemplo n.º 10
0
 def __init__(self, constraint, maxLength=None, mutable=None):
     self.constraint = IConstraint(constraint)
     self.maxLength = maxLength
     self.mutable = mutable
Exemplo n.º 11
0
 def __init__(self, constraint, maxLength=None, minLength=0):
     self.constraint = IConstraint(constraint)
     self.maxLength = maxLength
     self.minLength = minLength