Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: third_party/twisted_8_1/twisted/spread/flavors.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 This module represents flavors of remotely acessible objects.
8
9 Currently this is only objects accessible through Perspective Broker, but will
10 hopefully encompass all forms of remote access which can emulate subsets of PB
11 (such as XMLRPC or SOAP).
12
13 Future Plans: Optimization. Exploitation of new-style object model.
14 Optimizations to this module should not affect external-use semantics at all,
15 but may have a small impact on users who subclass and override methods.
16
17 @author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
18 """
19
20 __version__ = "$Revision: 1.32 $"[11:-2]
21
22 # NOTE: this module should NOT import pb; it is supposed to be a module which
23 # abstractly defines remotely accessible types. Many of these types expect to
24 # be serialized by Jelly, but they ought to be accessible through other
25 # mechanisms (like XMLRPC)
26
27 # system imports
28 import sys
29 from zope.interface import implements, Interface
30
31 # twisted imports
32 from twisted.python import log, reflect
33
34 # sibling imports
35 from jelly import setUnjellyableForClass, setUnjellyableForClassTree, setUnjelly ableFactoryForClass, unjellyableRegistry
36 from jelly import Jellyable, Unjellyable, _Dummy, _DummyNewStyle
37 from jelly import setInstanceState, getInstanceState
38
39 # compatibility
40 setCopierForClass = setUnjellyableForClass
41 setCopierForClassTree = setUnjellyableForClassTree
42 setFactoryForClass = setUnjellyableFactoryForClass
43 copyTags = unjellyableRegistry
44
45 copy_atom = "copy"
46 cache_atom = "cache"
47 cached_atom = "cached"
48 remote_atom = "remote"
49
50
51 class NoSuchMethod(AttributeError):
52 """Raised if there is no such remote method"""
53
54
55 class IPBRoot(Interface):
56 """Factory for root Referenceable objects for PB servers."""
57
58 def rootObject(broker):
59 """Return root Referenceable for broker."""
60
61
62 class Serializable(Jellyable):
63 """An object that can be passed remotely.
64
65 I am a style of object which can be serialized by Perspective
66 Broker. Objects which wish to be referenceable or copied remotely
67 have to subclass Serializable. However, clients of Perspective
68 Broker will probably not want to directly subclass Serializable; the
69 Flavors of transferable objects are listed below.
70
71 What it means to be \"Serializable\" is that an object can be
72 passed to or returned from a remote method. Certain basic types
73 (dictionaries, lists, tuples, numbers, strings) are serializable by
74 default; however, classes need to choose a specific serialization
75 style: L{Referenceable}, L{Viewable}, L{Copyable} or L{Cacheable}.
76
77 You may also pass C{[lists, dictionaries, tuples]} of L{Serializable}
78 instances to or return them from remote methods, as many levels deep
79 as you like.
80 """
81
82 def processUniqueID(self):
83 """Return an ID which uniquely represents this object for this process.
84
85 By default, this uses the 'id' builtin, but can be overridden to
86 indicate that two values are identity-equivalent (such as proxies
87 for the same object).
88 """
89
90 return id(self)
91
92 class Referenceable(Serializable):
93 perspective = None
94 """I am an object sent remotely as a direct reference.
95
96 When one of my subclasses is sent as an argument to or returned
97 from a remote method call, I will be serialized by default as a
98 direct reference.
99
100 This means that the peer will be able to call methods on me;
101 a method call xxx() from my peer will be resolved to methods
102 of the name remote_xxx.
103 """
104
105 def remoteMessageReceived(self, broker, message, args, kw):
106 """A remote message has been received. Dispatch it appropriately.
107
108 The default implementation is to dispatch to a method called
109 'remote_messagename' and call it with the same arguments.
110 """
111 args = broker.unserialize(args)
112 kw = broker.unserialize(kw)
113 method = getattr(self, "remote_%s" % message, None)
114 if method is None:
115 raise NoSuchMethod("No such method: remote_%s" % (message,))
116 try:
117 state = method(*args, **kw)
118 except TypeError:
119 log.msg("%s didn't accept %s and %s" % (method, args, kw))
120 raise
121 return broker.serialize(state, self.perspective)
122
123 def jellyFor(self, jellier):
124 """(internal)
125
126 Return a tuple which will be used as the s-expression to
127 serialize this to a peer.
128 """
129
130 return "remote", jellier.invoker.registerReference(self)
131
132
133 class Root(Referenceable):
134 """I provide a root object to L{pb.Broker}s for a L{pb.BrokerFactory}.
135
136 When a L{pb.BrokerFactory} produces a L{pb.Broker}, it supplies that
137 L{pb.Broker} with an object named \"root\". That object is obtained
138 by calling my rootObject method.
139
140 See also: L{pb.getObjectAt}
141 """
142
143 implements(IPBRoot)
144
145 def rootObject(self, broker):
146 """A L{pb.BrokerFactory} is requesting to publish me as a root object.
147
148 When a L{pb.BrokerFactory} is sending me as the root object, this
149 method will be invoked to allow per-broker versions of an
150 object. By default I return myself.
151 """
152 return self
153
154
155 class ViewPoint(Referenceable):
156 """
157 I act as an indirect reference to an object accessed through a
158 L{pb.Perspective}.
159
160 Simply put, I combine an object with a perspective so that when a
161 peer calls methods on the object I refer to, the method will be
162 invoked with that perspective as a first argument, so that it can
163 know who is calling it.
164
165 While L{Viewable} objects will be converted to ViewPoints by default
166 when they are returned from or sent as arguments to a remote
167 method, any object may be manually proxied as well. (XXX: Now that
168 this class is no longer named C{Proxy}, this is the only occourance
169 of the term 'proxied' in this docstring, and may be unclear.)
170
171 This can be useful when dealing with L{pb.Perspective}s, L{Copyable}s,
172 and L{Cacheable}s. It is legal to implement a method as such on
173 a perspective::
174
175 | def perspective_getViewPointForOther(self, name):
176 | defr = self.service.getPerspectiveRequest(name)
177 | defr.addCallbacks(lambda x, self=self: ViewPoint(self, x), log.msg)
178 | return defr
179
180 This will allow you to have references to Perspective objects in two
181 different ways. One is through the initial 'attach' call -- each
182 peer will have a L{pb.RemoteReference} to their perspective directly. The
183 other is through this method; each peer can get a L{pb.RemoteReference} to
184 all other perspectives in the service; but that L{pb.RemoteReference} will
185 be to a L{ViewPoint}, not directly to the object.
186
187 The practical offshoot of this is that you can implement 2 varieties
188 of remotely callable methods on this Perspective; view_xxx and
189 C{perspective_xxx}. C{view_xxx} methods will follow the rules for
190 ViewPoint methods (see ViewPoint.L{remoteMessageReceived}), and
191 C{perspective_xxx} methods will follow the rules for Perspective
192 methods.
193 """
194
195 def __init__(self, perspective, object):
196 """Initialize me with a Perspective and an Object.
197 """
198 self.perspective = perspective
199 self.object = object
200
201 def processUniqueID(self):
202 """Return an ID unique to a proxy for this perspective+object combinatio n.
203 """
204 return (id(self.perspective), id(self.object))
205
206 def remoteMessageReceived(self, broker, message, args, kw):
207 """A remote message has been received. Dispatch it appropriately.
208
209 The default implementation is to dispatch to a method called
210 'C{view_messagename}' to my Object and call it on my object with
211 the same arguments, modified by inserting my Perspective as
212 the first argument.
213 """
214 args = broker.unserialize(args, self.perspective)
215 kw = broker.unserialize(kw, self.perspective)
216 method = getattr(self.object, "view_%s" % message)
217 try:
218 state = apply(method, (self.perspective,)+args, kw)
219 except TypeError:
220 log.msg("%s didn't accept %s and %s" % (method, args, kw))
221 raise
222 rv = broker.serialize(state, self.perspective, method, args, kw)
223 return rv
224
225
226 class Viewable(Serializable):
227 """I will be converted to a L{ViewPoint} when passed to or returned from a r emote method.
228
229 The beginning of a peer's interaction with a PB Service is always
230 through a perspective. However, if a C{perspective_xxx} method returns
231 a Viewable, it will be serialized to the peer as a response to that
232 method.
233 """
234
235 def jellyFor(self, jellier):
236 """Serialize a L{ViewPoint} for me and the perspective of the given brok er.
237 """
238 return ViewPoint(jellier.invoker.serializingPerspective, self).jellyFor( jellier)
239
240
241
242 class Copyable(Serializable):
243 """Subclass me to get copied each time you are returned from or passed to a remote method.
244
245 When I am returned from or passed to a remote method call, I will be
246 converted into data via a set of callbacks (see my methods for more
247 info). That data will then be serialized using Jelly, and sent to
248 the peer.
249
250 The peer will then look up the type to represent this with; see
251 L{RemoteCopy} for details.
252 """
253
254 def getStateToCopy(self):
255 """Gather state to send when I am serialized for a peer.
256
257 I will default to returning self.__dict__. Override this to
258 customize this behavior.
259 """
260
261 return self.__dict__
262
263 def getStateToCopyFor(self, perspective):
264 """
265 Gather state to send when I am serialized for a particular
266 perspective.
267
268 I will default to calling L{getStateToCopy}. Override this to
269 customize this behavior.
270 """
271
272 return self.getStateToCopy()
273
274 def getTypeToCopy(self):
275 """Determine what type tag to send for me.
276
277 By default, send the string representation of my class
278 (package.module.Class); normally this is adequate, but
279 you may override this to change it.
280 """
281
282 return reflect.qual(self.__class__)
283
284 def getTypeToCopyFor(self, perspective):
285 """Determine what type tag to send for me.
286
287 By default, defer to self.L{getTypeToCopy}() normally this is
288 adequate, but you may override this to change it.
289 """
290
291 return self.getTypeToCopy()
292
293 def jellyFor(self, jellier):
294 """Assemble type tag and state to copy for this broker.
295
296 This will call L{getTypeToCopyFor} and L{getStateToCopy}, and
297 return an appropriate s-expression to represent me.
298 """
299
300 if jellier.invoker is None:
301 return getInstanceState(self, jellier)
302 p = jellier.invoker.serializingPerspective
303 t = self.getTypeToCopyFor(p)
304 state = self.getStateToCopyFor(p)
305 sxp = jellier.prepare(self)
306 sxp.extend([t, jellier.jelly(state)])
307 return jellier.preserve(self, sxp)
308
309
310 class Cacheable(Copyable):
311 """A cached instance.
312
313 This means that it's copied; but there is some logic to make sure
314 that it's only copied once. Additionally, when state is retrieved,
315 it is passed a "proto-reference" to the state as it will exist on
316 the client.
317
318 XXX: The documentation for this class needs work, but it's the most
319 complex part of PB and it is inherently difficult to explain.
320 """
321
322 def getStateToCacheAndObserveFor(self, perspective, observer):
323 """
324 Get state to cache on the client and client-cache reference
325 to observe locally.
326
327 This is similiar to getStateToCopyFor, but it additionally
328 passes in a reference to the client-side RemoteCache instance
329 that will be created when it is unserialized. This allows
330 Cacheable instances to keep their RemoteCaches up to date when
331 they change, such that no changes can occur between the point
332 at which the state is initially copied and the client receives
333 it that are not propogated.
334 """
335
336 return self.getStateToCopyFor(perspective)
337
338 def jellyFor(self, jellier):
339 """Return an appropriate tuple to serialize me.
340
341 Depending on whether this broker has cached me or not, this may
342 return either a full state or a reference to an existing cache.
343 """
344 if jellier.invoker is None:
345 return getInstanceState(self, jellier)
346 luid = jellier.invoker.cachedRemotelyAs(self, 1)
347 if luid is None:
348 luid = jellier.invoker.cacheRemotely(self)
349 p = jellier.invoker.serializingPerspective
350 type_ = self.getTypeToCopyFor(p)
351 observer = RemoteCacheObserver(jellier.invoker, self, p)
352 state = self.getStateToCacheAndObserveFor(p, observer)
353 l = jellier.prepare(self)
354 jstate = jellier.jelly(state)
355 l.extend([type_, luid, jstate])
356 return jellier.preserve(self, l)
357 else:
358 return cached_atom, luid
359
360 def stoppedObserving(self, perspective, observer):
361 """This method is called when a client has stopped observing me.
362
363 The 'observer' argument is the same as that passed in to
364 getStateToCacheAndObserveFor.
365 """
366
367
368
369 class RemoteCopy(Unjellyable):
370 """I am a remote copy of a Copyable object.
371
372 When the state from a L{Copyable} object is received, an instance will
373 be created based on the copy tags table (see setUnjellyableForClass) and
374 sent the L{setCopyableState} message. I provide a reasonable default
375 implementation of that message; subclass me if you wish to serve as
376 a copier for remote data.
377
378 NOTE: copiers are invoked with no arguments. Do not implement a
379 constructor which requires args in a subclass of L{RemoteCopy}!
380 """
381
382 def setCopyableState(self, state):
383 """I will be invoked with the state to copy locally.
384
385 'state' is the data returned from the remote object's
386 'getStateToCopyFor' method, which will often be the remote
387 object's dictionary (or a filtered approximation of it depending
388 on my peer's perspective).
389 """
390
391 self.__dict__ = state
392
393 def unjellyFor(self, unjellier, jellyList):
394 if unjellier.invoker is None:
395 return setInstanceState(self, unjellier, jellyList)
396 self.setCopyableState(unjellier.unjelly(jellyList[1]))
397 return self
398
399
400
401 class RemoteCache(RemoteCopy, Serializable):
402 """A cache is a local representation of a remote L{Cacheable} object.
403
404 This represents the last known state of this object. It may
405 also have methods invoked on it -- in order to update caches,
406 the cached class generates a L{pb.RemoteReference} to this object as
407 it is originally sent.
408
409 Much like copy, I will be invoked with no arguments. Do not
410 implement a constructor that requires arguments in one of my
411 subclasses.
412 """
413
414 def remoteMessageReceived(self, broker, message, args, kw):
415 """A remote message has been received. Dispatch it appropriately.
416
417 The default implementation is to dispatch to a method called
418 'C{observe_messagename}' and call it on my with the same arguments.
419 """
420
421 args = broker.unserialize(args)
422 kw = broker.unserialize(kw)
423 method = getattr(self, "observe_%s" % message)
424 try:
425 state = apply(method, args, kw)
426 except TypeError:
427 log.msg("%s didn't accept %s and %s" % (method, args, kw))
428 raise
429 return broker.serialize(state, None, method, args, kw)
430
431 def jellyFor(self, jellier):
432 """serialize me (only for the broker I'm for) as the original cached ref erence
433 """
434 if jellier.invoker is None:
435 return getInstanceState(self, jellier)
436 assert jellier.invoker is self.broker, "You cannot exchange cached proxi es between brokers."
437 return 'lcache', self.luid
438
439
440 def unjellyFor(self, unjellier, jellyList):
441 if unjellier.invoker is None:
442 return setInstanceState(self, unjellier, jellyList)
443 self.broker = unjellier.invoker
444 self.luid = jellyList[1]
445 if isinstance(self.__class__, type): #new-style class
446 cProxy = _DummyNewStyle()
447 else:
448 cProxy = _Dummy()
449 cProxy.__class__ = self.__class__
450 cProxy.__dict__ = self.__dict__
451 # XXX questionable whether this was a good design idea...
452 init = getattr(cProxy, "__init__", None)
453 if init:
454 init()
455 unjellier.invoker.cacheLocally(jellyList[1], self)
456 cProxy.setCopyableState(unjellier.unjelly(jellyList[2]))
457 # Might have changed due to setCopyableState method; we'll assume that
458 # it's bad form to do so afterwards.
459 self.__dict__ = cProxy.__dict__
460 # chomp, chomp -- some existing code uses "self.__dict__ =", some uses
461 # "__dict__.update". This is here in order to handle both cases.
462 self.broker = unjellier.invoker
463 self.luid = jellyList[1]
464 return cProxy
465
466 ## def __really_del__(self):
467 ## """Final finalization call, made after all remote references have bee n lost.
468 ## """
469
470 def __cmp__(self, other):
471 """Compare me [to another RemoteCache.
472 """
473 if isinstance(other, self.__class__):
474 return cmp(id(self.__dict__), id(other.__dict__))
475 else:
476 return cmp(id(self.__dict__), other)
477
478 def __hash__(self):
479 """Hash me.
480 """
481 return int(id(self.__dict__) % sys.maxint)
482
483 broker = None
484 luid = None
485
486 def __del__(self):
487 """Do distributed reference counting on finalize.
488 """
489 try:
490 # log.msg( ' --- decache: %s %s' % (self, self.luid) )
491 if self.broker:
492 self.broker.decCacheRef(self.luid)
493 except:
494 log.deferr()
495
496 def unjellyCached(unjellier, unjellyList):
497 luid = unjellyList[1]
498 cNotProxy = unjellier.invoker.cachedLocallyAs(luid)
499
500 cProxy = _Dummy()
501 cProxy.__class__ = cNotProxy.__class__
502 cProxy.__dict__ = cNotProxy.__dict__
503 return cProxy
504
505 setUnjellyableForClass("cached", unjellyCached)
506
507 def unjellyLCache(unjellier, unjellyList):
508 luid = unjellyList[1]
509 obj = unjellier.invoker.remotelyCachedForLUID(luid)
510 return obj
511
512 setUnjellyableForClass("lcache", unjellyLCache)
513
514 def unjellyLocal(unjellier, unjellyList):
515 obj = unjellier.invoker.localObjectForID(unjellyList[1])
516 return obj
517
518 setUnjellyableForClass("local", unjellyLocal)
519
520 class RemoteCacheMethod:
521 """A method on a reference to a L{RemoteCache}.
522 """
523
524 def __init__(self, name, broker, cached, perspective):
525 """(internal) initialize.
526 """
527 self.name = name
528 self.broker = broker
529 self.perspective = perspective
530 self.cached = cached
531
532 def __cmp__(self, other):
533 return cmp((self.name, self.broker, self.perspective, self.cached), othe r)
534
535 def __hash__(self):
536 return hash((self.name, self.broker, self.perspective, self.cached))
537
538 def __call__(self, *args, **kw):
539 """(internal) action method.
540 """
541 cacheID = self.broker.cachedRemotelyAs(self.cached)
542 if cacheID is None:
543 from pb import ProtocolError
544 raise ProtocolError("You can't call a cached method when the object hasn't been given to the peer yet.")
545 return self.broker._sendMessage('cache', self.perspective, cacheID, self .name, args, kw)
546
547 class RemoteCacheObserver:
548 """I am a reverse-reference to the peer's L{RemoteCache}.
549
550 I am generated automatically when a cache is serialized. I
551 represent a reference to the client's L{RemoteCache} object that
552 will represent a particular L{Cacheable}; I am the additional
553 object passed to getStateToCacheAndObserveFor.
554 """
555
556 def __init__(self, broker, cached, perspective):
557 """(internal) Initialize me.
558
559 @param broker: a L{pb.Broker} instance.
560
561 @param cached: a L{Cacheable} instance that this L{RemoteCacheObserver}
562 corresponds to.
563
564 @param perspective: a reference to the perspective who is observing this .
565 """
566
567 self.broker = broker
568 self.cached = cached
569 self.perspective = perspective
570
571 def __repr__(self):
572 return "<RemoteCacheObserver(%s, %s, %s) at %s>" % (
573 self.broker, self.cached, self.perspective, id(self))
574
575 def __hash__(self):
576 """Generate a hash unique to all L{RemoteCacheObserver}s for this broker /perspective/cached triplet
577 """
578
579 return ( (hash(self.broker) % 2**10)
580 + (hash(self.perspective) % 2**10)
581 + (hash(self.cached) % 2**10))
582
583 def __cmp__(self, other):
584 """Compare me to another L{RemoteCacheObserver}.
585 """
586
587 return cmp((self.broker, self.perspective, self.cached), other)
588
589 def callRemote(self, _name, *args, **kw):
590 """(internal) action method.
591 """
592 cacheID = self.broker.cachedRemotelyAs(self.cached)
593 if cacheID is None:
594 from pb import ProtocolError
595 raise ProtocolError("You can't call a cached method when the "
596 "object hasn't been given to the peer yet.")
597 return self.broker._sendMessage('cache', self.perspective, cacheID,
598 _name, args, kw)
599
600 def remoteMethod(self, key):
601 """Get a L{pb.RemoteMethod} for this key.
602 """
603 return RemoteCacheMethod(key, self.broker, self.cached, self.perspective )
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/spread/banana.py ('k') | third_party/twisted_8_1/twisted/spread/interfaces.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698