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
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)
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)
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")])
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))
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 __init__(self, *alternatives): self.alternatives = [IConstraint(a) for a in alternatives] self.alternatives = tuple(self.alternatives)
def __init__(self, *elemConstraints): self.constraints = [IConstraint(e) for e in elemConstraints]
def __init__(self, keyConstraint, valueConstraint, maxKeys=None): self.keyConstraint = IConstraint(keyConstraint) self.valueConstraint = IConstraint(valueConstraint) self.maxKeys = maxKeys
def __init__(self, constraint, maxLength=None, mutable=None): self.constraint = IConstraint(constraint) self.maxLength = maxLength self.mutable = mutable
def __init__(self, constraint, maxLength=None, minLength=0): self.constraint = IConstraint(constraint) self.maxLength = maxLength self.minLength = minLength