How do I use a Pyro proxy object as a factory?
I want to use Pyro with an existing set of classes that includes a factory pattern, that is, an object of class A (usually there will only be one of them) is used to create objects of class B (there can be an arbitrary number of them) using the factory method. Thus, I expose the class A object as a Pyro proxy object.
I've expanded on Pyro's introductory code sample to reflect what I'm trying to do. The server side code looks like this:
# 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 code has also been slightly modified:
# 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 instantiate NewObj
and manipulate them in the same way I can manipulate instances GreetingMaker
on the client side, but it looks like what happens when a method make_obj
gets called, a NewObj
is created on the server side, goes out of scope immediately and, therefore, garbage is collected.
It looks like this, 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 can work around this problem if the factory class (i.e. GreetingMaker
) contains a reference to each one created NewObj
and adds a cleanup method of some type ... but is this really necessary? Am I missing something in Pyro that can help me implement this?
(edited for clarity)
source to share
The problem is that it pyro
sucks the object NewObj
on the server side, but it cannot flatten it out on the client side because the implementation is NewObj
unknown to the client.
One way to fix the problem is to create a third module, for example, new_obj.py
and then import it on both the server and client as follows:
from new_obj import NewObj
This will allow the client to deploy NewObj
and work with the instance . In any case, please note that this will be a real object NewObj
living on the client and not a proxy for an object living on the server.
source to share
I recently came across this feature and am using it. This is very important for my code that 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()
#...
source to share