我有一个构建系统,必须从用户打包protobufs'投入。用户填写表单中的字段,提交它们,构建系统打包protobufs并沿着它们的方式发送它们。构建系统必须不知道它正在处理的类型。否则,每次项目团队更改他们的.protos时,我都必须进行构建更改以支持他们。
绝大部分都是这样的。 .protos可能看起来像这样:
#dataobj_base.proto
message DataObj {
extensions 100 to 199;
optional string property1 = 1;
}
#dataobj.proto
message DataObjExtension {
optional string ext_property1 = 1;
}
extend DataObj {
optional DataObjExtension generic_extension = 100;
}
....这很简单,足以支持。到目前为止,我的构建系统已经通过知道它正在编写的类型的名称来访问generic_extension对象,仅此而已。我可以导入DataObj_pb2和DataObjExtension_pb2并像这样使用它们:
def do_the_thing(type_name, property1, ext_property1):
#type_name, in this case, is 'DataObj'
game_module = (
importlib.import_module('generated_protobufs.{}_pb2'.format(type_name)))
ext_module = (
importlib.import_module('generated_protobufs.{}Extension_pb2'.format(type_name)))
instance_constructor = getattr(game_module, type_name)
instance = instance_constructor()
#instance is now an instance of DataObj
instance.property1 = property1
setattr(pb_instance.Extensions[ext_module.generic_extension],
property_name, property_value)
}
这很有效,但项目团队希望从同一个基础派生多个类型。如果我需要两种与DataObj" base一起使用的不同类型,它就会中断。"我希望能够做到这一点:
extend DataObj {
optional SomeOtherBaseObj generic_extension = 100;
}
麻烦的是,在我的python代码中,我需要知道generic_extension
的类型。如果我正在编写具有我正在处理的类型的知识的代码,我会这样做:
extensions = instance.Extensions[SomeOtherBaseObj_pb2.generic_extension]
......而extensions.__class__
会告诉我我需要知道的一切。
有没有办法在不首先了解它们的情况下获取扩展中任何属性的名称 - 包括(尤其)它们的类型信息?我可以通过一百万种方式重新设计.protos是一项巨大的改进,但已经有相当多的代码基础依赖于它们。添加字段会很好,我可能会出售小的修改,但是不可能进行大的结构更改。
即使我只能获得扩展程序中的属性列表,我也是金色的。我这样修理它:
extend DataObj {
optional SomeOtherBaseObj generic_extension = 100;
optional bool generic_extension_type__SomeOtherBaseObj = 101;
}
generic_extension_type __...将无缘无故,除了给我一个generic_extension类型的名称。但是,如果我能做到这一点,我可以将其简化为:
extend DataObj {
optional SomeOtherBaseObj generic_extension__SomeOtherBaseObj = 100;
}
答案 0 :(得分:2)
instance.ListFields()
列出邮件实例上设置的所有字段,包括扩展名。列表中的每个项目都是FieldDescriptor
的元组 - 其中包含完整类型信息 - 以及字段值。 (FieldDescriptor
也是您用作Extensions
地图的关键字的内容。)您可以这样做:
for field, value in instance.ListFields():
assert value == instance.Extensions[field]
print field.full_name, value
如果您要查找邮件中已有的扩展程序,您可以选择以下几种选项:
DataObj._extensions_by_name
是一个dict,用于将相应扩展名的完全限定的Protobuf符号名称(如my_pkg.generic_extension
)映射到FieldDescriptor
。
DataObj._extensions_by_number
是一个字典映射字段编号(在您的情况下为100到199)到相应扩展名的FieldDescriptor
。
你当然可以迭代任何一个dict来了解所有已知的扩展。
不幸的是,这两者在技术上都是私有的,这意味着他们可能在未来的版本中破解。目前AFAIK没有公共界面。但是,这些成员自第一个版本以来就已存在,并且存在于纯Python和C扩展支持的实现中。而且,整个"扩展"功能已在proto3中删除,所以担心这个界面消失似乎没有实际意义。
另请注意,依靠这些"全球注册机构"它被认为是不太好的设计。更好的设计是初始化组件的代码,以传递它需要知道的所有扩展的列表。一些高级代码应该轮询程序的其他组件,询问他们需要什么扩展,构建一个详尽的列表,然后将其传入。这种方法可以更容易地知道实际使用哪些扩展而不是哪些扩展。 #34;死码"可以删除。但是,我认识到现有的代码库并不总是如此精心编写,所以实现了正确的方式"正确的方式"可能比它的价值更麻烦。