Dear Twisted users,

To my amazement I have reproduced this strange behaviour in an even simpler
program. Attached are a pb client/server which illustrate unexpected
behaviour of the id of a pb.RemoteCache. To run the example, run server.py
and then client.py (with resources.py in the same working directory).

You will see output like this (line numbers added by me)

====
1. RemoteCache 29403544 initialized
2. Client received RemoteCache: id=29403544
3. RemoteCache: while responding to observe_add I think my id is 29403760
4. Client: The RemoteCache's ide is 29403544
...
====

1. When the RemoteCache is initialized it thinks its id is 29403544.
2. The client agrees that it has received a RemoteCache with id 29403544.
3. When the RemoteCache is inside observe_add it thinks its id is 29403760.
4. The client still thinks the RemoteCache's id is 29403544.

If you let the program run it will continue to loop between the RemoteCache
reporting its id while in observe_add, and the client reporting the id of
the RemoteCache. The two reported ids are always the same unequal numbers.

The question appears to be "Why does the id reported by a RemoteCache's
while inside an observe_* method differ the id reported by objects owning
references to that RemoteCache?"

Thank you for your time,
Daniel




On Wed, Apr 30, 2014 at 11:00 AM, Daniel Sank <sank.dan...@gmail.com> wrote:

> 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
>



-- 
Daniel Sank
Department of Physics
Broida Hall
University of California
Santa Barbara, CA 93117
(805)893-3899
import weakref

import resources
from twisted.internet import reactor

def main():
    c = resources.Client(reactor)
    c.connect()
    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

class Cacheable(pb.Cacheable):
    """I'm a cacheable object which notifies observers when data is added"""
    def __init__(self):
        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 None


class RemoteCache(pb.RemoteCache):
    """I find out about data added to the Cacheable"""
    def __init__(self):
            print("RemoteCache %d initialized"%(id(self),))
    
    def setCopyableState(self, state):
        pass
    
    def observe_add(self, obj):
        msg = "RemoteCache: while responding to observe_add I think my id is"
        print("%s %d"%(msg, id(self)))


pb.setUnjellyableForClass(Cacheable, RemoteCache)


class Server(object):
    implements(portal.IRealm)
        
    def __init__(self):
        self.thing = Cacheable()
        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
    
    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):
        self.perspective = perspective
        self.reactor.callLater(1.0, self.start)
    
    def start(self):
        self.perspective.callRemote("start")
    
    def update(self):
        self.perspective.callRemote("update")
        self.reactor.callLater(1, self.showId)
    
    def remote_take(self, d):
        self.d = d
        print("Client received RemoteCache: id=%d"%(id(d),))
        self.reactor.callLater(2.0, self.update)
    
    def showId(self):
        print("Client: The RemoteCache's id is %d"%id(self.d))
        self.reactor.callLater(1, self.update)
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()
_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to