使用Pyro5代理获取对象时出现“不支持的序列化类”
我正在更新一些代码,从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 个回答
我现在回答我自己的问题,因为我已经搞明白了,希望将来有其他人遇到同样的问题时能有所帮助。
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())