bugfix in proto descriptor creation

reference cycles in betterproto messages have led to infinite recursion
This commit is contained in:
Erik Friese 2023-08-31 17:17:28 +02:00
parent 441844b97a
commit bdd3389b17

View File

@ -20,25 +20,36 @@ pub fn create_cached_descriptor(obj: &PyAny) -> Result<MessageDescriptor> {
.lock() .lock()
.unwrap(); .unwrap();
create_cached_message_in_pool(obj, &mut pool)
}
fn create_cached_message_in_pool(
obj: &PyAny,
pool: &mut DescriptorPool,
) -> Result<MessageDescriptor> {
let name = obj.qualified_class_name()?; let name = obj.qualified_class_name()?;
if let Some(desc) = pool.get_message_by_name(&name) { if let Some(desc) = pool.get_message_by_name(&name) {
return Ok(desc); return Ok(desc);
} }
let meta = obj.get_proto_meta()?; let mut file = FileDescriptorProto {
let mut message = DescriptorProto {
name: Some(name.clone()), name: Some(name.clone()),
..Default::default() ..Default::default()
}; };
add_message_to_file(name.clone(), obj, &pool, &mut file)?;
pool.add_file_descriptor_proto(file)?;
Ok(pool.get_message_by_name(&name).expect("Just registered..."))
}
fn add_message_to_file(
name: String,
obj: &PyAny,
pool: &DescriptorPool,
file: &mut FileDescriptorProto,
) -> Result<()> {
let mut messages_to_add = vec![(name, obj)];
while let Some((name, obj)) = messages_to_add.pop() {
let meta = obj.get_proto_meta()?;
let mut message = DescriptorProto {
name: Some(name.to_string()),
..Default::default()
};
for item in meta for item in meta
.getattr("meta_by_field_name")? .getattr("meta_by_field_name")?
.call_method0("items")? .call_method0("items")?
@ -57,16 +68,42 @@ fn create_cached_message_in_pool(
match field.r#type() { match field.r#type() {
Type::Message => { Type::Message => {
let instance = meta.create_instance(field_name)?; let cls = meta.get_class(field_name)?;
let cls_name = instance.qualified_class_name()?; let cls_name = cls.qualified_name()?;
field.type_name = Some(cls_name.to_string()); field.type_name = Some(cls_name.clone());
create_cached_message_in_pool(instance, pool)?;
if name != cls_name
&& pool.get_message_by_name(&cls_name).is_none()
&& !file.message_type.iter().any(|item| item.name() == cls_name)
&& !messages_to_add.iter().any(|item| item.0 == cls_name)
{
messages_to_add.push((cls_name, cls.call0()?));
}
} }
Type::Enum => { Type::Enum => {
let cls = meta.get_class(field_name)?; let cls = meta.get_class(field_name)?;
let cls_name = cls.qualified_name()?; let cls_name = cls.qualified_name()?;
field.type_name = Some(cls_name.to_string()); field.type_name = Some(cls_name.to_string());
create_cached_enum_in_pool(cls, pool)?;
if pool.get_enum_by_name(&cls_name).is_none()
&& !file.enum_type.iter().any(|item| item.name() == cls_name)
{
let mut proto = EnumDescriptorProto {
name: Some(cls_name),
..Default::default()
};
for item in cls.iter()? {
let item = item?;
proto.value.push(EnumValueDescriptorProto {
number: Some(item.getattr("value")?.extract()?),
name: Some(item.getattr("name")?.extract()?),
..Default::default()
});
}
file.enum_type.push(proto);
}
} }
_ => {} _ => {}
} }
@ -96,40 +133,8 @@ fn create_cached_message_in_pool(
}); });
} }
pool.add_file_descriptor_proto(FileDescriptorProto { file.message_type.push(message);
name: Some(name.clone()),
message_type: vec![message],
..Default::default()
})?;
Ok(pool.get_message_by_name(&name).expect("Just registered..."))
} }
fn create_cached_enum_in_pool(cls: &PyAny, pool: &mut DescriptorPool) -> Result<()> {
let cls_name = cls.qualified_name()?;
if pool.get_enum_by_name(&cls_name).is_some() {
return Ok(());
}
let mut proto = EnumDescriptorProto {
name: Some(cls_name.clone()),
..Default::default()
};
for item in cls.iter()? {
let item = item?;
proto.value.push(EnumValueDescriptorProto {
number: Some(item.getattr("value")?.extract()?),
name: Some(item.getattr("name")?.extract()?),
..Default::default()
});
}
pool.add_file_descriptor_proto(FileDescriptorProto {
name: Some(cls_name),
enum_type: vec![proto],
..Default::default()
})?;
Ok(()) Ok(())
} }