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

Reply via email to