我正在更新一些代码以使用 Pyro5(来自 Pyro3),并且看不到如何处理从通过 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()
改为返回字符串,则一切OK。有没有办法让我的类 Item
被序列化和反序列化,而无需大量额外的自定义代码?既然serpent处理得还可以,而且是Pyro5用的,看来这应该不会太复杂!
既然我已经弄清楚了,就回答我自己的问题,以防将来其他人遇到同样的问题。
Pyro3(我认为 Pyro4 的早期版本)使用 Pickle 来序列化和反序列化对象,这能够重新创建各种自定义对象,包括它们包含的函数。这可能会导致安全问题,因为 Pickle 在从可能不可信的源反序列化对象时会执行任意代码,因此 Pyro4 的最新版本使用 Serpent 代替,而 Pickle 在 Pyro5 中根本不可用。
在我原来的例子中,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())