使用Pyro5代理获取对象时出现“不支持的序列化类”

1 投票
1 回答
27 浏览
提问于 2025-04-14 16:06

我正在更新一些代码,从Pyro3切换到Pyro5,但我不知道怎么处理通过Pyro代理返回的自定义对象。为了演示,我在一个文件classes.py中创建了两个简单的类:Container有一个方法可以返回Item的实例:

from Pyro5.api import expose

class Container:
    @expose
    def get_item(self):
        return Item()

class Item():
    def __str__(self):
        return "item"

serpent这个包可以对Item类型的对象进行序列化和反序列化,也就是把对象转换成可以存储或传输的格式,然后再转换回来:

import serpent
from classes import Item

item = Item()
s = serpent.dumps(item)

item2 = serpent.loads(s)
print(item2)

运行这个代码,结果是我们预期的:

{'__class__': 'Item'}

接下来,我创建了一个Pyro5守护进程,并注册了一个Container的实例:

from Pyro5.api import Daemon
from classes import Container
    
daemon = Daemon(host="localhost", port=5555)
daemon.register(Container(),"container")
daemon.requestLoop()

但是当我尝试通过代理获取Item的实例时,像这样:

from Pyro5.api import Proxy

container = Proxy("PYRO:container@localhost:5555")
print(container.get_item())

我遇到了一个异常错误。

Pyro5.errors.SerializeError: unsupported serialized class: classes.Item

如果把get_item()改成返回一个字符串,那就没问题了。有没有办法让我的Item类在没有很多额外自定义代码的情况下进行序列化和反序列化?因为serpent处理得很好,而且Pyro5也在使用它,所以这应该不会太复杂!

1 个回答

0

我现在回答我自己的问题,因为我已经搞明白了,希望将来有其他人遇到同样的问题时能有所帮助。

Pyro3(我想Pyro4的早期版本也是这样)使用了Pickle来对对象进行序列化和反序列化,这样可以重建各种自定义对象,包括它们包含的函数。不过,这样做可能会带来安全隐患,因为Pickle在从不可信的来源反序列化对象时,会执行任意代码。因此,Pyro4的后期版本改用了Serpent,而在Pyro5中完全不再使用Pickle。

在我最初的例子中,Serpent并没有重建一个Item对象,而只是创建了一个包含对象字段的字典。Pyro试图将这个字典转换成Item对象,但不知道该怎么做,所以就出现了“不支持的序列化类”的错误。

推荐的做法是使用内置类型,而不是自定义类(https://pyro5.readthedocs.io/en/latest/clientcode.html#serialization)。另外,也可以给Pyro代理提供一个函数,将字典转换回所需的对象。在我这个没有实例状态的简单例子中,这非常简单:

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

撰写回答