3

I want to use Pyro with an existing set of classes that involve a factory pattern, i.e. an object of Class A (typically there will only be one of these) is used to instantiate objects of Class B (there can be an arbitrary number of these) through a factory method . So, I'm exposing an object of Class A as the Pyro proxy object.

I've extended the Pyro introductory sample code to reflect roughly what I'm trying to do. The server-side code is as follows:

# saved as greeting.py
import Pyro4
import socket

class NewObj:
    func_count = None
    def __init__(self):
    print "{0} ctor".format(self)
        func_count = 0
    def __del__(self):
    print "{0} dtor".format(self)
    def func(self):
    print "{0} func call {1}".format(self, self.func_count)
    self.func_count += 1

class GreetingMaker(object):
    def __init__(self):
    print "{0} ctor".format(self)
    def __del__(self):
    print "{0} dtor".format(self)
    def get_fortune(self, name):
    print "getting fortune"
        return "Hello, {0}. Here is your fortune message:\n" \
               "Behold the warranty -- the bold print giveth and the fine print taketh away.".format(name)
    def make_obj(self):
    return NewObj()

greeting_maker=GreetingMaker()

daemon=Pyro4.Daemon(host=socket.gethostbyname(socket.gethostname()), port=8080)                                      # make a Pyro daemon
uri=daemon.register(greeting_maker, "foo")  # register the greeting object as a Pyro object

print "Ready. Object uri =", uri            # print the uri so we can use it in the client later
daemon.requestLoop()                        # start the event loop of the server to wait for calls

The client side code was also altered slightly:

# saved as client.py
import Pyro4

uri="PYRO:foo@10.2.129.6:8080"
name="foo"

greeting_maker=Pyro4.Proxy(uri)          # get a Pyro proxy to the greeting object
print greeting_maker.get_fortune(name)   # call method normally
print greeting_maker.make_obj()

My intention is to be able to create instances of NewObj and to manipulate them just as I can manipulate instances of GreetingMaker on the client side, but it looks as though what happens is when the make_obj method gets called, a NewObj is created on the server side, immediately falls out of scope, and is consequently garbage collected.

This is what the output looks like, server-side:

<__main__.GreetingMaker object at 0x2aed47e01110> ctor
/usr/lib/python2.6/site-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py:152: UserWarning: HMAC_KEY not set, protocol data may not be secure
  warnings.warn("HMAC_KEY not set, protocol data may not be secure")
Ready. Object uri = PYRO:foo@10.2.129.6:8080
getting fortune
<__main__.NewObj instance at 0x175c8098> ctor
<__main__.NewObj instance at 0x175c8098> dtor

... and client-side:

/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py:152: UserWarning: HMAC_KEY not set, protocol data may not be secure
  warnings.warn("HMAC_KEY not set, protocol data may not be secure")
Hello, foo. Here is your fortune message:
Behold the warranty -- the bold print giveth and the fine print taketh away.
Traceback (most recent call last):
  File "client.py", line 9, in <module>
    print greeting_maker.make_obj()
  File "/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py", line 146, in __call__
    return self.__send(self.__name, args, kwargs)
  File "/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/core.py", line 269, in _pyroInvoke
    data=self._pyroSerializer.deserialize(data, compressed=flags & MessageFactory.FLAGS_COMPRESSED)
  File "/usr/local/lib/python2.6/dist-packages/Pyro4-4.12-py2.6.egg/Pyro4/util.py", line 146, in deserialize
    return self.pickle.loads(data)
AttributeError: 'module' object has no attribute 'NewObj'

I suspect I could hack around this problem by having the factory class (i.e. GreetingMaker) keep a reference to every NewObj that it creates, and add a cleanup method of some sort... but is that really necessary? Am I missing something in Pyro that can help me implement this?

(edited for clarity)

Community
  • 1
  • 1
tramdas
  • 448
  • 2
  • 10

2 Answers2

2

I recently came across this feature and am using it. It is crucial for my code which uses a similar factory pattern.

Pyro Server

class Foo(object):
    def __init__(self, x=5):
        self.x = x

class Server(object):
    def build_foo(self, x=5):
        foo = Foo(x)
        # This line will register your foo instance as its own proxy
        self._pyroDaemon.register(foo)
        # Returning foo here returns the proxy, not the actual foo
        return foo

#...
uri = daemon.register(Server()) # In the later versions, just use Server, not Server()
#...
Matthew Moisen
  • 16,701
  • 27
  • 128
  • 231
0

The problem here is that pyro pickles the NewObj object in the server side, but it fails to unpickle it in the client side because the NewObj implementation is unknown to the client.

One way to fix the problem, would be to create a third module called, for example new_obj.py and, after that, import it in both the server and the client as follows:

from new_obj import NewObj

This will let the client unpickle the NewObj instance and work with it. Anyway, please note that it will be a real NewObj object living in the client, not a proxy to an object living in the server.

jcollado
  • 39,419
  • 8
  • 102
  • 133