def checkToken(self, typebyte, size): if self.classname is None: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("InstanceUnslicer classname must be string") elif self.attrname is None: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("InstanceUnslicer keys must be STRINGs")
def checkToken(self, typebyte, size): if self.key is None: if typebyte != tokens.INT: raise BananaError("VocabUnslicer only accepts INT keys") else: if typebyte != tokens.STRING: raise BananaError("VocabUnslicer only accepts STRING values")
def checkToken(self, typebyte, size): if self.request == None: if typebyte != tokens.INT: raise BananaError("request ID must be an INT") elif not self.gotFailure: self.fConstraint.checkToken(typebyte, size) else: raise BananaError("stop sending me stuff!")
def checkToken(self, typebyte, size): if self.state == 0: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("MethodUnslicer methodname must be a string") elif self.state == 1: if typebyte != tokens.OPEN: raise BananaError("MethodUnslicer instance must be OPEN") elif self.state == 2: if typebyte != tokens.OPEN: raise BananaError("MethodUnslicer class must be an OPEN")
def doOpen(self, opentype): # check the opentype if self.state == 1: if opentype[0] not in ("instance", "none"): raise BananaError("MethodUnslicer instance must be " + "instance or None") elif self.state == 2: if opentype[0] != "class": raise BananaError("MethodUnslicer class must be a class") unslicer = self.open(opentype) # TODO: apply constraint return unslicer
def receiveKey(self, key): # I don't think it is legal (in python) to use an incomplete object # as a dictionary key, because you must have all the contents to # hash it. Someone could fake up a token stream to hit this case, # however: OPEN(dict), OPEN(tuple), OPEN(reference), 0, CLOSE, CLOSE, # "value", CLOSE if isinstance(key, Deferred): raise BananaError("incomplete object as dictionary key") try: if self.d.has_key(key): raise BananaError("duplicate key '%s'" % key) except TypeError: raise BananaError("unhashable key '%s'" % key) self.key = key
def handleViolation(self, f, methname, inOpen=False, inClose=False): """An Unslicer has decided to give up, or we have given up on it (because we received an ABORT token). """ where = self.describeReceive() f.value.setLocation(where) if self.debugReceive: print " handleViolation-%s (inOpen=%s, inClose=%s): %s" \ % (methname, inOpen, inClose, f) assert isinstance(f, BananaFailure) if self.logViolations: log.msg("Violation in %s at %s" % (methname, where)) log.err(f) if inOpen: self.discardCount += 1 if self.debugReceive: print " ++discardCount (inOpen), now %d" % self.discardCount while True: # tell the parent that their child is dead. This is useful for # things like PB, which may want to errback the current request. if self.debugReceive: print " reportViolation to %s" % self.receiveStack[-1] f = self.receiveStack[-1].reportViolation(f) if not f: # they absorbed the failure if self.debugReceive: print " buck stopped, error absorbed" break # the old top wants to propagate it upwards if self.debugReceive: print " popping %s" % self.receiveStack[-1] if not inClose: self.discardCount += 1 if self.debugReceive: print " ++discardCount (pop, not inClose), now %d" \ % self.discardCount inClose = False old = self.receiveStack.pop() try: # TODO: if handleClose encountered a Violation in .finish, # we will end up calling it a second time old.finish() # ?? except Violation: pass # they've already failed once if not self.receiveStack: # now there's nobody left to create new Unslicers, so we # must drop the connection why = "Oh my god, you killed the RootUnslicer! " + \ "You bastard!!" raise BananaError(why)
def receiveClose(self): # three things must happen before our request is complete: # receiveClose has occurred # the receiveChild object deferred (if any) has fired # ready_deferred has finished # If ready_deferred errbacks, provide its failure object to the # request. If not, provide the request with whatever receiveChild # got. if not self._child_deferred: raise BananaError("Answer didn't include an answer") if self._ready_deferreds: d = AsyncAND(self._ready_deferreds) else: d = defer.succeed(None) def _ready(res): return self._child_deferred d.addCallback(_ready) def _done(res): self.request.complete(res) def _fail(f): # we hit here if any of the _ready_deferreds fail (i.e a Gift # failed to resolve), or if the _child_deferred fails (not sure # how this could happen). I think it's ok to return a local # exception (instead of a RemoteException) for both. self.request.fail(f) d.addCallbacks(_done, _fail) return None, None
def checkToken(self, typebyte, size): if self.attrname == None: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("RemoteCopyUnslicer keys must be STRINGs") else: if self.attrConstraint: self.attrConstraint.checkToken(typebyte, size)
def receiveChild(self, obj, ready_deferred=None): assert not isinstance(obj, Deferred) assert ready_deferred is None if self.finished: raise BananaError("FunctionUnslicer only accepts one string") self.finished = True # TODO: taste here! self.func = reflect.namedObject(obj)
def checkToken(self, typebyte, size): # TODO: limit strings by returning a number instead of None if self.stage == 0: if typebyte != tokens.INT: raise BananaError("request ID must be an INT") elif self.stage == 1: if typebyte not in (tokens.INT, tokens.NEG): raise BananaError("object ID must be an INT/NEG") elif self.stage == 2: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("method name must be a STRING") # TODO: limit to longest method name of self.obj in the interface elif self.stage == 3: if typebyte != tokens.OPEN: raise BananaError("arguments must be an 'arguments' sequence") else: raise BananaError("too many objects given to CallUnslicer")
def receiveChild(self, obj, ready_deferred=None): assert ready_deferred is None if self.classname is None: self.classname = obj self.attrname = None elif self.attrname is None: self.attrname = obj else: if isinstance(obj, Deferred): # TODO: this is an artificial restriction, and it might # be possible to remove it, but I need to think through # it carefully first raise BananaError("unreferenceable object in attribute") if self.d.has_key(self.attrname): raise BananaError("duplicate attribute name '%s'" % name) self.setAttribute(self.attrname, obj) self.attrname = None
def receiveChild(self, obj, ready_deferred=None): assert not isinstance(obj, Deferred) assert ready_deferred is None if self.finished: raise BananaError("ModuleUnslicer only accepts one string") self.finished = True # TODO: taste here! mod = __import__(obj, {}, {}, "x") self.mod = mod
def receiveChild(self, token, ready_deferred=None): assert not isinstance(token, Deferred) assert ready_deferred is None if self.key is None: if self.d.has_key(token): raise BananaError("duplicate key '%s'" % token) self.key = token else: self.d[self.key] = token self.key = None
def receiveChild(self, obj, ready_deferred=None): assert not isinstance(obj, Deferred) assert ready_deferred is None if self.finished: raise BananaError("ReferenceUnslicer only accepts one int") self.obj = self.protocol.getObject(obj) self.finished = True # assert that this conforms to the constraint if self.constraint: self.constraint.checkObject(self.obj, True)
def checkToken(self, typebyte, size): # TODO: limit strings by returning a number instead of None if self.stage == 0: if typebyte != tokens.INT: raise BananaError("request ID must be an INT") elif self.stage == 1: if typebyte not in (tokens.INT, tokens.NEG): raise BananaError("object ID must be an INT/NEG") elif self.stage == 2: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("method name must be a STRING") # TODO: limit to longest method name of self.obj in the interface elif self.stage == 3: if self.argname == None: if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("argument name must be a STRING") # TODO: limit to the longest argname in the method else: if self.argConstraint: self.argConstraint.checkToken(typebyte, size)
def receiveClose(self): if self.stage != 4: raise BananaError("'call' sequence ended too early") # time to create the InboundDelivery object so we can queue it delivery = InboundDelivery(self.broker, self.reqID, self.obj, self.interface, self.methodname, self.methodSchema, self.allargs) ready_deferred = None if self._ready_deferreds: ready_deferred = AsyncAND(self._ready_deferreds) return delivery, ready_deferred
def checkToken(self, typebyte, size): if self.numargs is None: # waiting for positional-arg count if typebyte != tokens.INT: raise BananaError("posarg count must be an INT") return if len(self.args) < self.numargs: # waiting for a positional arg if self.argConstraint: self.argConstraint.checkToken(typebyte, size) return if self.argname is None: # waiting for the name of a keyword arg if typebyte not in (tokens.STRING, tokens.VOCAB): raise BananaError("kwarg name must be a STRING") # TODO: limit to longest argument name of the method? return # waiting for the value of a kwarg if self.argConstraint: self.argConstraint.checkToken(typebyte, size)
def receiveChild(self, obj, ready_deferred=None): assert not isinstance(obj, defer.Deferred) assert ready_deferred is None if self.attrname == None: attrname = obj if self.d.has_key(attrname): raise BananaError("duplicate attribute name '%s'" % attrname) s = self.schema if s: accept, self.attrConstraint = s.getAttrConstraint(attrname) assert accept self.attrname = attrname else: if isinstance(obj, defer.Deferred): # TODO: this is an artificial restriction, and it might # be possible to remove it, but I need to think through # it carefully first raise BananaError("unreferenceable object in attribute") self.setAttribute(self.attrname, obj) self.attrname = None self.attrConstraint = None
def openerCheckToken(self, typebyte, size, opentype): if typebyte == tokens.STRING: if size > self.maxIndexLength: why = "STRING token is too long, %d>%d" % \ (size, self.maxIndexLength) raise Violation(why) elif typebyte == tokens.VOCAB: return else: # TODO: hack for testing raise Violation("index token 0x%02x not STRING or VOCAB" % \ ord(typebyte)) raise BananaError("index token 0x%02x not STRING or VOCAB" % \ ord(typebyte))
def receiveClose(self): if self.state != 3: raise BananaError("MethodUnslicer requires three objects") if self.im_self is None: meth = getattr(self.im_class, self.im_func) # getattr gives us an unbound method return meth, None # TODO: late-available instances #if isinstance(self.im_self, NotKnown): # im = _InstanceMethod(self.im_name, self.im_self, self.im_class) # return im meth = self.im_class.__dict__[self.im_func] # whereas __dict__ gives us a function im = instancemethod(meth, self.im_self, self.im_class) return im, None
def receiveChild(self, obj, ready_deferred=None): assert not isinstance(obj, Deferred) assert ready_deferred is None if self.state == 0: self.im_func = obj self.state = 1 elif self.state == 1: assert type(obj) in (types.InstanceType, types.NoneType) self.im_self = obj self.state = 2 elif self.state == 2: assert type(obj) == types.ClassType # TODO: new-style classes? self.im_class = obj self.state = 3 else: raise BananaError("MethodUnslicer only accepts three objects")
def handleClose(self, closeCount): if self.debugReceive: print "handleClose(%d)" % closeCount if self.receiveStack[-1].openCount != closeCount: raise BananaError("lost sync, got CLOSE(%d) but expecting %s" \ % (closeCount, self.receiveStack[-1].openCount)) child = self.receiveStack[-1] # don't pop yet: describe() needs it try: obj, ready_deferred = child.receiveClose() except Violation: # the child is contaminated. However, they're finished, so we # don't have to discard anything. Just give an Failure to the # parent instead of the object they would have returned. f = BananaFailure() self.handleViolation(f, "receiveClose", inClose=True) return if self.debugReceive: print "receiveClose returned", obj try: child.finish() except Violation: # .finish could raise a Violation if an object that references # the child is just now deciding that they don't like it # (perhaps their TupleConstraint couldn't be asserted until the # tuple was complete and referenceable). In this case, the child # has produced a valid object, but an earlier (incomplete) # object is not valid. So we treat this as if this child itself # raised the Violation. The .where attribute will point to this # child, which is the node that caused somebody problems, but # will be marked <FINISH>, which indicates that it wasn't the # child itself which raised the Violation. TODO: not true # # TODO: it would be more useful if the UF could also point to # the completing object (the one which raised Violation). f = BananaFailure() self.handleViolation(f, "finish", inClose=True) return self.receiveStack.pop() # now deliver the object to the parent self.handleToken(obj, ready_deferred)
def handleClose(self, closeCount): if self.debugReceive: print "handleClose(%d)" % closeCount if self.receiveStack[-1].openCount != closeCount: raise BananaError("lost sync, got CLOSE(%d) but expecting %s" \ % (closeCount, self.receiveStack[-1].openCount)) child = self.receiveStack[-1] # don't pop yet: describe() needs it try: obj, ready_deferred = child.receiveClose() except Violation, v: # the child is contaminated. However, they're finished, so we # don't have to discard anything. Just give an Failure to the # parent instead of the object they would have returned. f = BananaFailure() self.handleViolation(f, "receiveClose", inClose=True) return
def receiveClose(self): if self.debug: log.msg("%s.receiveClose: %s %s %s" % (self, self.closed, self.num_unreferenceable_children, len(self._ready_deferreds))) if (self.numargs is None or len(self.args) < self.numargs or self.argname is not None): raise BananaError("'arguments' sequence ended too early") self.closed = True dl = [] if self.num_unreferenceable_children: d = self._all_children_are_referenceable_d = defer.Deferred() dl.append(d) dl.extend(self._ready_deferreds) ready_deferred = None if dl: ready_deferred = AsyncAND(dl) return self, ready_deferred
def checkToken(self, typebyte, size): if self.request is None: if typebyte != tokens.INT: raise BananaError("request ID must be an INT") elif not self.haveResults: if self.resultConstraint: try: self.resultConstraint.checkToken(typebyte, size) except Violation, v: # improve the error message if v.args: # this += gives me a TypeError "object doesn't # support item assignment", which confuses me #v.args[0] += " in inbound method results" why = v.args[0] + " in inbound method results" v.args = why, else: v.args = ("in inbound method results", ) raise v # this will errback the request
def handleSendViolation(self, f, doPop, sendAbort): f.value.setLocation(self.describeSend()) while True: top = self.slicerStack[-1][0] if self.debugSend: print " handleSendViolation.loop, top=%s" % top # should we send an ABORT? Only if an OPEN has been sent, which # happens in pushSlicer (if at all). if sendAbort: lastOpenID = self.slicerStack[-1][2] if lastOpenID is not None: if self.debugSend: print " sending ABORT(%s)" % lastOpenID self.sendAbort(lastOpenID) # should we pop the Slicer? yes if doPop: if self.debugSend: print " popping %s" % top self.popSlicer() if not self.slicerStack: if self.debugSend: print "RootSlicer died!" raise BananaError("Hey! You killed the RootSlicer!") top = self.slicerStack[-1][0] # now inform the parent. If they also give up, we will # loop, popping more Slicers off the stack until the # RootSlicer ignores the error if self.debugSend: print " notifying parent", top f = top.childAborted(f) if f: doPop = True sendAbort = True continue else: break
def receiveClose(self): if self.stage != 3 or self.argname != None: raise BananaError("'call' sequence ended too early") self.stage = 4 return self.checkComplete()
def receiveChild(self, token, ready_deferred=None): if self.stage < 3: assert not isinstance(token, defer.Deferred) assert ready_deferred is None #print "CallUnslicer.receiveChild [s%d]" % self.stage, repr(token) # TODO: if possible, return an error to the other side if self.stage == 0: # reqID # we don't yet know which reqID to send any failure to self.reqID = token self.stage += 1 assert not self.broker.activeLocalCalls.get(self.reqID) self.broker.activeLocalCalls[self.reqID] = self return if self.stage == 1: # objID # this might raise an exception if objID is invalid self.objID = token self.obj = self.broker.getMyReferenceByCLID(token) #iface = self.broker.getRemoteInterfaceByName(token) if self.objID < 0: self.interface = None else: self.interface = self.obj.getInterface() self.stage = 2 return if self.stage == 2: # methodname # validate the methodname, get the schema. This may raise an # exception for unknown methods if self.objID < 0: # the target is a bound method self.methodSchema = getattr(self.obj, "methodSchema", None) self.methodname = None # TODO: give it something useful if self.broker.requireSchema and not self.methodSchema: why = "This broker does not accept unconstrained " + \ "method calls" raise Violation(why) self.stage = 3 return methodname = token # must find the schema, using the interfaces # TODO: getSchema should probably be in an adapter instead of in # a pb.Referenceable base class. Old-style (unconstrained) # flavors.Referenceable should be adapted to something which # always returns None # TODO: make this faster. A likely optimization is to take a # tuple of components.getInterfaces(obj) and use it as a cache # key. It would be even faster to use obj.__class__, but that # would probably violate the expectation that instances can # define their own __implements__ (independently from their # class). If this expectation were to go away, a quick # obj.__class__ -> RemoteReferenceSchema cache could be built. ms = None if self.interface: # they are calling an interface+method pair ms = self.interface.get(methodname) if not ms: why = "method '%s' not defined in %s" % \ (methodname, self.interface.__remote_name__) raise Violation(why) self.methodSchema = ms self.methodname = methodname if self.broker.requireSchema and not self.methodSchema: why = "This broker does not accept unconstrained method calls" raise Violation(why) self.stage = 3 return if self.stage == 3: # argname/value pairs if self.argname == None: assert not isinstance(token, defer.Deferred) assert ready_deferred is None argname = token if self.args.has_key(argname): raise BananaError("duplicate argument '%s'" % argname) ms = self.methodSchema if ms: # if the argname is invalid, this may raise Violation accept, self.argConstraint = ms.getArgConstraint(argname) assert accept # TODO: discard if not self.argname = argname else: argvalue = token if isinstance(argvalue, defer.Deferred): self.num_unreferenceable_children += 1 argvalue.addCallback(self.update, self.argname) argvalue.addErrback(self.explode) self.args[self.argname] = argvalue self.argname = None if ready_deferred: self.num_unready_children += 1 ready_deferred.addCallback(self.ready)
if self.resultConstraint: try: self.resultConstraint.checkToken(typebyte, size) except Violation, v: # improve the error message if v.args: # this += gives me a TypeError "object doesn't # support item assignment", which confuses me #v.args[0] += " in inbound method results" why = v.args[0] + " in inbound method results" v.args = why, else: v.args = ("in inbound method results", ) raise v # this will errback the request else: raise BananaError("stop sending me stuff!") def doOpen(self, opentype): if self.resultConstraint: self.resultConstraint.checkOpentype(opentype) # TODO: improve the error message unslicer = self.open(opentype) if unslicer: if self.resultConstraint: unslicer.setConstraint(self.resultConstraint) return unslicer def receiveChild(self, token, ready_deferred=None): assert not isinstance(token, defer.Deferred) assert ready_deferred is None if self.request == None: