Dear twisted users,
I think I have found some surprising behavior in perspective broker. I
define a subclass of pb.RemoteCache which has a method managed by a
descriptor. The descriptor keeps track of the instances it manages in a set.
When the RemoteCache is first received by the client, it accesses the
descriptor-ified method inside setCopyableState. At that time the
RemoteCache's id is a certain value X. Then later when the RemoteCache is
notified of a change by the server side Cacheable, it again accesses the
descriptor-ified method, but at this time it's id is Y, and Y!=X.
I attach to this post a simple working example which displays the behavior
described above. To run, first run server.py and then run client.py. You
will see a little bit of output, the most important part being
===
Client received RemoteCache: id=45952496
Asking server to update
TD 45921680 (test.RemoteCache.add) accessed by <test.RemoteCache instance
at 2BD2DC8>: id=45952456
RemoteCache id=45952496
===
To understand the details of the output please see descriptor.py. The id of
the RemoteCache is first reported as 45952496 when the client receives it.
Then, when the descriptor is accessed, the id of the accessing instance is
reported as 45952456, which is different. Then, when in the last line we
print out the id of the RemoteCache we're back to 45952496.
Is there some reason that a RemoteCache's id can change during its life
time?
--
Daniel Sank
Department of Physics
Broida Hall
University of California
Santa Barbara, CA 93117
(805)893-3899
import twisted.spread.pb as pb
import resources
from twisted.internet import reactor
from twisted.cred import portal, checkers
def main():
realm = resources.Server()
checker = checkers.InMemoryUsernamePasswordDatabaseDontUse()
checker.addUser("alice", "1234")
p = portal.Portal(realm, [checker])
reactor.listenTCP(8800, pb.PBServerFactory(p))
reactor.run()
if __name__ == "__main__":
main()
import twisted.spread.pb as pb
import twisted.cred.portal as portal
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from twisted.cred import credentials
from zope.interface import implements
import descriptor
class Cacheable(pb.Cacheable):
"""I'm a cacheable object which notifies observers when data is added"""
def __init__(self, data):
self.data = data
self.observers = {}
def add(self, x):
for p in self.observers:
self.observers[p].callRemote("add", x)
def getStateToCacheAndObserveFor(self, perspective, observer):
self.observers[perspective] = observer
return self.data
class RemoteCache(pb.RemoteCache):
"""I find out about data added to the Cacheable"""
def __init__(self):
print("RemoteCache %d.__init__"%(id(self),))
def setCopyableState(self, state):
print
print("RemoteCache starting setCopyableState")
for elem in state:
self.add(elem)
print("RemoteCache finished setCopyableState")
print
def observe_add(self, obj):
self.add(obj)
@descriptor.TestDescriptor
def add(self, obj):
print("RemoteCache %d added %s"%(id(self), obj))
pb.setUnjellyableForClass(Cacheable, RemoteCache)
class Server(object):
implements(portal.IRealm)
def __init__(self):
self.thing = Cacheable(['a', 'hello'])
self.users = set()
def requestAvatar(self, avatarID, mind, *interfaces):
assert pb.IPerspective in interfaces
p = Perspective(avatarID, mind, self)
self.users.add(p)
return pb.IPerspective, p, lambda a=p:a.detached()
def start(self):
for p in self.users:
p.mind.callRemote("take", self.thing)
def update(self):
self.thing.add("foobar")
class Perspective(pb.Avatar):
def __init__(self, name, mind, server):
self.name = name
self.mind = mind
self.server = server
def detached(self):
self.mind = None
def perspective_start(self):
self.server.start()
def perspective_update(self):
self.server.update()
class Client(pb.Referenceable):
def __init__(self, reactor):
self.reactor = reactor
print("Client %d"%(id(self),))
def connect(self):
factory = pb.PBClientFactory()
reactor.connectTCP("localhost", 8800, factory)
def1 = factory.login(credentials.UsernamePassword("alice", "1234"),
client=self)
def1.addCallback(self.connected)
def connected(self, perspective):
print("CONNECTED")
self.perspective = perspective
self.reactor.callLater(1.0, self.start)
def start(self):
self.perspective.callRemote("start")
def update(self):
print("Asking server to update")
self.perspective.callRemote("update")
self.reactor.callLater(1, self.showId)
def remote_take(self, d):
self.d = d
print
print("Client received RemoteCache: id=%d"%(id(d),))
print
self.reactor.callLater(2.0, self.update)
def showId(self):
print("RemoteCache id=%d"%id(self.d))
import weakref
import resources
from twisted.internet import reactor
def main():
c = resources.Client(reactor)
c.connect()
reactor.run()
if __name__ == "__main__":
main()
import functools
class TestDescriptor(object):
def __init__(self, func):
self.instances = set()
self._func = func
def __get__(self, inst, cls):
if inst is None:
return self
print("TD %d (%s.%s) accessed by %s: id=%d"%(id(self), cls,
self._func.__name__, inst, id(inst)))
if inst in self.instances:
print("has already seen %d"%id(inst))
else:
self.instances.add(inst)
return functools.partial(self._func, inst)
_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python