Search code examples
pythonserializationpyro

"Unsupported serialized class" when using Pyro5 proxy to get object


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!


Solution

  • 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())