Package foolscap :: Module call
[hide private]
[frames] | no frames]

Source Code for Module foolscap.call

  1   
  2  from twisted.python import failure, reflect 
  3  from twisted.internet import defer 
  4   
  5  from foolscap import copyable, slicer, tokens 
  6  from foolscap.copyable import AttributeDictConstraint 
  7  from foolscap.constraint import ByteStringConstraint 
  8  from foolscap.slicers.list import ListConstraint 
  9  from tokens import BananaError, Violation 
 10  from foolscap.util import AsyncAND 
 11  from foolscap.logging import log 
 12   
 13   
14 -class FailureConstraint(AttributeDictConstraint):
15 opentypes = [("copyable", "twisted.python.failure.Failure")] 16 name = "FailureConstraint" 17 klass = failure.Failure 18
19 - def __init__(self):
20 attrs = [('type', ByteStringConstraint(200)), 21 ('value', ByteStringConstraint(1000)), 22 ('traceback', ByteStringConstraint(2000)), 23 ('parents', ListConstraint(ByteStringConstraint(200))), 24 ] 25 AttributeDictConstraint.__init__(self, *attrs)
26
27 - def checkObject(self, obj, inbound):
28 if not isinstance(obj, self.klass): 29 raise Violation("is not an instance of %s" % self.klass)
30 31
32 -class PendingRequest(object):
33 # this object is a local representation of a message we have sent to 34 # someone else, that will be executed on their end. 35 active = True 36
37 - def __init__(self, reqID, rref=None):
38 self.reqID = reqID 39 self.rref = rref # keep it alive 40 self.broker = None # if set, the broker knows about us 41 self.deferred = defer.Deferred() 42 self.constraint = None # this constrains the results 43 self.failure = None
44
45 - def setConstraint(self, constraint):
47
48 - def complete(self, res):
49 if self.broker: 50 self.broker.removeRequest(self) 51 if self.active: 52 self.active = False 53 self.deferred.callback(res) 54 else: 55 log.msg("PendingRequest.complete called on an inactive request")
56
57 - def fail(self, why):
58 if self.active: 59 if self.broker: 60 self.broker.removeRequest(self) 61 self.active = False 62 self.failure = why 63 if (self.broker and 64 self.broker.tub and 65 self.broker.tub.logRemoteFailures): 66 67 my_short_tubid = "??" 68 if self.broker.tub: # for tests 69 my_short_tubid = self.broker.tub.getShortTubID() 70 their_short_tubid = self.broker.remote_tubref.getShortTubID() 71 72 lp = log.msg("an outbound callRemote (that we [%s] sent to " 73 "someone else [%s]) failed on the far end" 74 % (my_short_tubid, their_short_tubid), 75 level=log.UNUSUAL) 76 methname = ".".join([self.interfaceName or "?", 77 self.methodName or "?"]) 78 log.msg(" reqID=%d, rref=%s, methname=%s" 79 % (self.reqID, self.rref, methname), 80 level=log.NOISY, parent=lp) 81 #stack = why.getTraceback() 82 # TODO: include the first few letters of the remote tubID in 83 # this REMOTE tag 84 #stack = "REMOTE: " + stack.replace("\n", "\nREMOTE: ") 85 log.msg(" the REMOTE failure was:", failure=why, 86 level=log.NOISY, parent=lp) 87 #log.msg(stack, level=log.NOISY, parent=lp) 88 self.deferred.errback(why) 89 else: 90 log.msg("WEIRD: fail() on an inactive request", traceback=True) 91 if self.failure: 92 log.msg("multiple failures") 93 log.msg("first one was:", self.failure) 94 log.msg("this one was:", why) 95 log.err("multiple failures indicate a problem")
96
97 -class ArgumentSlicer(slicer.ScopedSlicer):
98 opentype = ('arguments',) 99
100 - def __init__(self, args, kwargs):
101 slicer.ScopedSlicer.__init__(self, None) 102 self.args = args 103 self.kwargs = kwargs 104 self.which = ""
105
106 - def sliceBody(self, streamable, banana):
107 yield len(self.args) 108 for i,arg in enumerate(self.args): 109 self.which = "arg[%d]" % i 110 yield arg 111 keys = self.kwargs.keys() 112 keys.sort() 113 for argname in keys: 114 self.which = "arg[%s]" % argname 115 yield argname 116 yield self.kwargs[argname]
117
118 - def describe(self):
119 return "<%s>" % self.which
120 121
122 -class CallSlicer(slicer.ScopedSlicer):
123 opentype = ('call',) 124
125 - def __init__(self, reqID, clid, methodname, args, kwargs):
126 slicer.ScopedSlicer.__init__(self, None) 127 self.reqID = reqID 128 self.clid = clid 129 self.methodname = methodname 130 self.args = args 131 self.kwargs = kwargs
132
133 - def sliceBody(self, streamable, banana):
134 yield self.reqID 135 yield self.clid 136 yield self.methodname 137 yield ArgumentSlicer(self.args, self.kwargs)
138
139 - def describe(self):
140 return "<call-%s-%s-%s>" % (self.reqID, self.clid, self.methodname)
141
142 -class InboundDelivery:
143 """An inbound message that has not yet been delivered. 144 145 This is created when a 'call' sequence has finished being received. The 146 Broker will add it to a queue. The delivery at the head of the queue is 147 serviced when all of its arguments have been resolved. 148 149 The only way that the arguments might not all be available is if one of 150 the Unslicers which created them has provided a 'ready_deferred' along 151 with the prospective object. The only standard Unslicer which does this 152 is the TheirReferenceUnslicer, which handles introductions. (custom 153 Unslicers might also provide a ready_deferred, for example a URL 154 slicer/unslicer pair for which the receiving end fetches the target of 155 the URL as its value, or a UnixFD slicer/unslicer that had to wait for a 156 side-channel unix-domain socket to finish transferring control over the 157 FD to the recipient before being ready). 158 159 Most Unslicers refuse to accept unready objects as their children (most 160 implementations of receiveChild() do 'assert ready_deferred is None'). 161 The CallUnslicer is fairly unique in not rejecting such objects. 162 163 We do require, however, that all of the arguments be at least 164 referenceable. This is not generally a problem: the only time an 165 unslicer's receiveChild() can get a non-referenceable object (represented 166 by a Deferred) is if that unslicer is participating in a reference cycle 167 that has not yet completed, and CallUnslicers only live at the top level, 168 above any cycles. 169 """ 170
171 - def __init__(self, broker, reqID, obj, 172 interface, methodname, methodSchema, 173 allargs):
174 self.broker = broker 175 self.reqID = reqID 176 self.obj = obj 177 self.interface = interface 178 self.methodname = methodname 179 self.methodSchema = methodSchema 180 self.allargs = allargs
181
182 - def logFailure(self, f):
183 # called if tub.logLocalFailures is True 184 my_short_tubid = "??" 185 if self.broker.tub: # for tests 186 my_short_tubid = self.broker.tub.getShortTubID() 187 their_short_tubid = "<unauth>" 188 if self.broker.remote_tubref: 189 their_short_tubid = self.broker.remote_tubref.getShortTubID() 190 lp = log.msg("an inbound callRemote that we [%s] executed (on behalf " 191 "of someone else, TubID %s) failed" 192 % (my_short_tubid, their_short_tubid), 193 level=log.UNUSUAL) 194 if self.interface: 195 methname = self.interface.getName() + "." + self.methodname 196 else: 197 methname = self.methodname 198 log.msg(" reqID=%d, rref=%s, methname=%s" % 199 (self.reqID, self.obj, methname), 200 level=log.NOISY, parent=lp) 201 log.msg(" args=%s" % (self.allargs.args,), level=log.NOISY, parent=lp) 202 log.msg(" kwargs=%s" % (self.allargs.kwargs,), 203 level=log.NOISY, parent=lp) 204 #if isinstance(f.type, str): 205 # stack = "getTraceback() not available for string exceptions\n" 206 #else: 207 # stack = f.getTraceback() 208 # TODO: trim stack to everything below Broker._doCall 209 #stack = "LOCAL: " + stack.replace("\n", "\nLOCAL: ") 210 log.msg(" the LOCAL failure was:", failure=f, 211 level=log.NOISY, parent=lp)
212 #log.msg(stack, level=log.NOISY, parent=lp) 213
214 -class ArgumentUnslicer(slicer.ScopedUnslicer):
215