I am updating some code to use Pyro5 (from Pyro3) and can't see how to deal with custom objects that are returned from a method accessed via a Pyro proxy. As a demonstration I have created two simple classes in a file classes.py
: Container
has a method that returns an instance of Item
:
from Pyro5.api import expose
class Container:
@expose
def get_item(self):
return Item()
class Item():
def __str__(self):
return "item"
The serpent
package can serialize and deserialize objects of type Item
:-
import serpent
from classes import Item
item = Item()
s = serpent.dumps(item)
item2 = serpent.loads(s)
print(item2)
Running this prints, as expected:
{'__class__': 'Item'}
Now I create a Pyro5 daemon and register an instance of Container
:
from Pyro5.api import Daemon
from classes import Container
daemon = Daemon(host="localhost", port=5555)
daemon.register(Container(),"container")
daemon.requestLoop()
But when I try to obtain an instance of Item
via the proxy like this
from Pyro5.api import Proxy
container = Proxy("PYRO:container@localhost:5555")
print(container.get_item())
I get an exception
Pyro5.errors.SerializeError: unsupported serialized class: classes.Item
If get_item()
is changed to return a string, all is OK. Is there a way to get my class Item
to be serialized and deserialized without a lot of extra custom code? Since serpent deals with it OK, and is used by Pyro5, it seems that this should not be too complex!
Answering my own question now that I have figured it out, in case someone else has the same problem in future.
Pyro3 (and I think earlier releases of Pyro4) used Pickle to serialize and deserialize objects, and this was able to recreate a wide range of custom objects, including the functions they contained. This can lead to security issues because Pickle will execute arbitrary code when deserializing an object from a potentially untrustworthy source, so late releases of Pyro4 use Serpent instead, and Pickle is not available at all in Pyro5.
In my original example, Serpent is not reconstructing an Item
object but just a dictionary containing the fields of the object. Pyro tries to convert this dictionary into an Item
object but doesn't know how, hence "unsupported serialized class".
The recommended approach (https://pyro5.readthedocs.io/en/latest/clientcode.html#serialization) is to use builtin types rather than custom classes. Alternatively it is possible to provide the Pyro proxy with a function to convert a dictionary back into the required object. In my artifiical example where the object has no instance state, this is very simple:
from Pyro5.api import Proxy, register_dict_to_class
from classes import Item
def item_dict_to_class(classname, d):
return Item()
register_dict_to_class("classes.Item", item_dict_to_class)
container = Proxy("PYRO:container@localhost:5555")
print(container.get_item())