1# -*- test-case-name: foolscap.test.test_banana -*- 2 3from __future__ import print_function 4from twisted.internet.defer import Deferred 5from foolscap.tokens import Violation 6from foolscap.slicer import BaseUnslicer 7from foolscap.slicers.list import ListSlicer 8from foolscap.constraint import OpenerConstraint, Any, IConstraint 9from foolscap.util import AsyncAND 10 11 12class TupleSlicer(ListSlicer): 13 opentype = ("tuple",) 14 slices = tuple 15 16class TupleUnslicer(BaseUnslicer): 17 opentype = ("tuple",) 18 19 debug = False 20 constraints = None 21 22 def setConstraint(self, constraint): 23 if isinstance(constraint, Any): 24 return 25 assert isinstance(constraint, TupleConstraint) 26 self.constraints = constraint.constraints 27 28 def start(self, count): 29 self.list = [] 30 # indices of .list which are unfilled because of children that could 31 # not yet be referenced 32 self.num_unreferenceable_children = 0 33 self.count = count 34 if self.debug: 35 print("%s[%d].start with %s" % (self, self.count, self.list)) 36 self.finished = False 37 self.deferred = Deferred() 38 self.protocol.setObject(count, self.deferred) 39 self._ready_deferreds = [] 40 41 def checkToken(self, typebyte, size): 42 if self.constraints == None: 43 return 44 if len(self.list) >= len(self.constraints): 45 raise Violation("the tuple is full") 46 self.constraints[len(self.list)].checkToken(typebyte, size) 47 48 def doOpen(self, opentype): 49 where = len(self.list) 50 if self.constraints != None: 51 if where >= len(self.constraints): 52 raise Violation("the tuple is full") 53 self.constraints[where].checkOpentype(opentype) 54 unslicer = self.open(opentype) 55 if unslicer: 56 if self.constraints != None: 57 unslicer.setConstraint(self.constraints[where]) 58 return unslicer 59 60 def update(self, obj, index): 61 if self.debug: 62 print("%s[%d].update: [%d]=%s" % (self, self.count, index, obj)) 63 self.list[index] = obj 64 self.num_unreferenceable_children -= 1 65 if self.finished: 66 self.checkComplete() 67 return obj 68 69 def receiveChild(self, obj, ready_deferred=None): 70 if ready_deferred: 71 self._ready_deferreds.append(ready_deferred) 72 if isinstance(obj, Deferred): 73 obj.addCallback(self.update, len(self.list)) 74 obj.addErrback(self.explode) 75 self.num_unreferenceable_children += 1 76 self.list.append("placeholder") 77 else: 78 self.list.append(obj) 79 80 def checkComplete(self): 81 if self.debug: 82 print("%s[%d].checkComplete: %d pending" % \ 83 (self, self.count, self.num_unreferenceable_children)) 84 if self.num_unreferenceable_children: 85 # not finished yet, we'll fire our Deferred when we are 86 if self.debug: 87 print(" not finished yet") 88 return 89 90 # list is now complete. We can finish. 91 return self.complete() 92 93 def complete(self): 94 ready_deferred = None 95 if self._ready_deferreds: 96 ready_deferred = AsyncAND(self._ready_deferreds) 97 98 t = tuple(self.list) 99 if self.debug: 100 print(" finished! tuple:%s{%s}" % (t, id(t))) 101 self.protocol.setObject(self.count, t) 102 self.deferred.callback(t) 103 return t, ready_deferred 104 105 def receiveClose(self): 106 if self.debug: 107 print("%s[%d].receiveClose" % (self, self.count)) 108 self.finished = 1 109 110 if self.num_unreferenceable_children: 111 # not finished yet, we'll fire our Deferred when we are 112 if self.debug: 113 print(" not finished yet") 114 ready_deferred = None 115 if self._ready_deferreds: 116 ready_deferred = AsyncAND(self._ready_deferreds) 117 return self.deferred, ready_deferred 118 119 # the list is already complete 120 return self.complete() 121 122 def describe(self): 123 return "[%d]" % len(self.list) 124 125 126class TupleConstraint(OpenerConstraint): 127 opentypes = [("tuple",)] 128 name = "TupleConstraint" 129 130 def __init__(self, *elemConstraints): 131 self.constraints = [IConstraint(e) for e in elemConstraints] 132 def checkObject(self, obj, inbound): 133 if not isinstance(obj, tuple): 134 raise Violation("not a tuple") 135 if len(obj) != len(self.constraints): 136 raise Violation("wrong size tuple") 137 for i in range(len(self.constraints)): 138 self.constraints[i].checkObject(obj[i], inbound) 139