1
2
3
4
5 from zope.interface import interface, implements
6 from twisted.python import reflect, log
7 from twisted.python.components import registerAdapter
8 from twisted.internet import defer
9
10 import slicer, tokens
11 from tokens import BananaError, Violation
12 from foolscap.constraint import OpenerConstraint, IConstraint, \
13 ByteStringConstraint, UnboundedSchema, Optional
14
15 Interface = interface.Interface
16
17
18
19
21 """I represent an object which is passed-by-value across PB connections.
22 """
23
25 """Return a string which names the class. This string must match the
26 one that gets registered at the receiving end. This is typically a
27 URL of some sort, in a namespace which you control."""
29 """Return a state dictionary (with plain-string keys) which will be
30 serialized and sent to the remote end. This state object will be
31 given to the receiving object's setCopyableState method."""
32
34 implements(ICopyable)
35
36
38 try:
39 copytype = self.typeToCopy
40 except AttributeError:
41 raise RuntimeError("Copyable subclasses must specify 'typeToCopy'")
42 return copytype
45
47 """I handle ICopyable objects (things which are copied by value)."""
48 - def slice(self, streamable, banana):
60 registerAdapter(CopyableSlicer, ICopyable, tokens.ISlicer)
61
62
64
65
66
68 return reflect.qual(self.__class__)
71 - def slice(self, streamable, banana):
78
79
80
81
83 """This is a shortcut for arranging to serialize third-party clases.
84 'copier' must be a callable which accepts an instance of the class you
85 want to serialize, and returns a tuple of (typename, state_dictionary).
86 If it returns a typename of None, the original class's fully-qualified
87 classname is used.
88 """
89 klassname = reflect.qual(klass)
90 class _CopierAdapter:
91 implements(ICopyable)
92 def __init__(self, original):
93 self.nameToCopy, self.state = copier(original)
94 if self.nameToCopy is None:
95 self.nameToCopy = klassname
96 def getTypeToCopy(self):
97 return self.nameToCopy
98 def getStateToCopy(self):
99 return self.state
100 registerAdapter(_CopierAdapter, klass, ICopyable)
101
102
103
104
106 attrname = None
107 attrConstraint = None
108
109 - def __init__(self, factory, stateSchema):
112
114 self.d = {}
115 self.count = count
116 self.deferred = defer.Deferred()
117 self.protocol.setObject(count, self.deferred)
118
126
135
157
160
162 try:
163 obj = self.factory(self.d)
164 except:
165 log.msg("%s.receiveClose: problem in factory %s" %
166 (self.__class__.__name__, self.factory))
167 log.err()
168 raise
169 self.protocol.setObject(self.count, obj)
170 self.deferred.callback(obj)
171 return obj, None
172
174 if self.classname == None:
175 return "<??>"
176 me = "<%s>" % self.classname
177 if self.attrname is None:
178 return "%s.attrname??" % me
179 else:
180 return "%s.%s" % (me, self.attrname)
181
182
184
185
186
187
188
189
190
192 self.d = {}
193 self.count = count
194 self.gettingAttrname = True
195
197 obj = self.factory(self.d)
198 return obj, None
199
200
202 """This interface defines what a RemoteCopy class must do. RemoteCopy
203 subclasses are used as factories to create objects that correspond to
204 Copyables sent over the wire.
205
206 Note that the constructor of an IRemoteCopy class will be called without
207 any arguments.
208 """
209
211 """I accept an attribute dictionary name/value pairs and use it to
212 set my internal state.
213
214 Some of the values may be Deferreds, which are placeholders for the
215 as-yet-unreferenceable object which will eventually go there. If you
216 receive a Deferred, you are responsible for adding a callback to
217 update the attribute when it fires. [note:
218 RemoteCopyUnslicer.receiveChild currently has a restriction which
219 prevents this from happening, but that may go away in the future]
220
221 Some of the objects referenced by the attribute values may have
222 Deferreds in them (e.g. containers which reference recursive tuples).
223 Such containers are responsible for updating their own state when
224 those Deferreds fire, but until that point their state is still
225 subject to change. Therefore you must be careful about how much state
226 inspection you perform within this method."""
227
228 stateSchema = interface.Attribute("""I return an AttributeDictConstraint
229 object which places restrictions on incoming attribute values. These
230 restrictions are enforced as the tokens are received, before the state is
231 passed to setCopyableState.""")
232
233
234
235 CopyableRegistry = {}
238 """Tell PB that unslicerfactory can be used to handle Copyable objects
239 that provide a getTypeToCopy name of 'typename'. 'unslicerfactory' must
240 be a callable which takes no arguments and returns an object which
241 provides IUnslicer.
242 """
243 assert callable(unslicerfactory)
244
245
246 test_unslicer = unslicerfactory()
247 assert tokens.IUnslicer.providedBy(test_unslicer)
248 assert type(typename) is str
249
250 if registry == None:
251 registry = CopyableRegistry
252 assert not registry.has_key(typename)
253 registry[typename] = unslicerfactory
254
255
256 debug_CopyableFactories = {}
259 """Tell PB that 'factory' can be used to handle Copyable objects that
260 provide a getTypeToCopy name of 'typename'. 'factory' must be a callable
261 which accepts a state dictionary and returns a fully-formed instance.
262
263 'cyclic' is a boolean, which should be set to False to avoid using a
264 Deferred to provide the resulting RemoteCopy instance. This is needed to
265 deserialize Failures (or instances which inherit from one, like
266 CopiedFailure). In exchange for this, it cannot handle reference cycles.
267 """
268 assert callable(factory)
269 debug_CopyableFactories[typename] = (factory, stateSchema, cyclic)
270 if cyclic:
271 def _RemoteCopyUnslicerFactory():
272 return RemoteCopyUnslicer(factory, stateSchema)
273 registerRemoteCopyUnslicerFactory(typename,
274 _RemoteCopyUnslicerFactory,
275 registry)
276 else:
277 def _RemoteCopyUnslicerFactoryNonCyclic():
278 return NonCyclicRemoteCopyUnslicer(factory, stateSchema)
279 registerRemoteCopyUnslicerFactory(typename,
280 _RemoteCopyUnslicerFactoryNonCyclic,
281 registry)
282
283
284
285
286 debug_RemoteCopyClasses = {}
288 """Tell PB that remote_copy_class is the appropriate RemoteCopy class to
289 use when deserializing a Copyable sequence that is tagged with
290 'typename'. 'remote_copy_class' should be a RemoteCopy subclass or
291 implement the same interface, which means its constructor takes no
292 arguments and it has a setCopyableState(state) method to actually set the
293 instance's state after initialization. It must also have a nonCyclic
294 attribute.
295 """
296 assert IRemoteCopy.implementedBy(remote_copy_class)
297 assert type(typename) is str
298
299