From 8f535913a11da6c9438e9bf37622d412c7050d1e Mon Sep 17 00:00:00 2001 From: Erik Friese Date: Tue, 5 Sep 2023 12:37:29 +0200 Subject: [PATCH] bugfix using python identifier in message names necessary to distinguish between dynamically created classes with same name --- betterproto-extras/src/descriptor_pool.rs | 10 ++++++---- betterproto-extras/src/py_any_extras.rs | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/betterproto-extras/src/descriptor_pool.rs b/betterproto-extras/src/descriptor_pool.rs index 2f02cb2..64a0b42 100644 --- a/betterproto-extras/src/descriptor_pool.rs +++ b/betterproto-extras/src/descriptor_pool.rs @@ -20,7 +20,8 @@ pub fn create_cached_descriptor(obj: &PyAny) -> Result { .lock() .unwrap(); - let name = obj.qualified_class_name()?; + let cls = obj.getattr("__class__")?; + let name = format!("{}_{}", cls.qualified_name()?, cls.py_identifier()); if let Some(desc) = pool.get_message_by_name(&name) { return Ok(desc); } @@ -159,6 +160,7 @@ fn add_message_to_file( Some("google.protobuf.Duration".to_string()); } _ => { + let cls_name = format!("{}_{}", cls_name, cls.py_identifier()); field.type_name = Some(cls_name.clone()); if message_name != cls_name @@ -179,14 +181,14 @@ fn add_message_to_file( }, Type::Enum => { let cls = meta.get_class(field_name)?; - let cls_name = cls.qualified_name()?; + let cls_name = format!("{}_{}", cls.qualified_name()?, cls.py_identifier()); field.type_name = Some(cls_name.to_string()); 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), + name: Some(cls_name.clone()), ..Default::default() }; @@ -194,7 +196,7 @@ fn add_message_to_file( let item = item?; proto.value.push(EnumValueDescriptorProto { number: Some(item.getattr("value")?.extract()?), - name: Some(item.getattr("name")?.extract()?), + name: Some(format!("{}_{}", cls_name, item.getattr("name")?.extract::<&str>()?)), ..Default::default() }); } diff --git a/betterproto-extras/src/py_any_extras.rs b/betterproto-extras/src/py_any_extras.rs index 1f4a881..998031b 100644 --- a/betterproto-extras/src/py_any_extras.rs +++ b/betterproto-extras/src/py_any_extras.rs @@ -1,5 +1,5 @@ use crate::error::Result; -use pyo3::PyAny; +use pyo3::{PyAny, Py, sync::GILOnceCell}; pub trait PyAnyExtras { fn qualified_name(&self) -> Result; @@ -9,6 +9,7 @@ pub trait PyAnyExtras { fn create_instance(&self, field_name: &str) -> Result<&PyAny>; fn is_list_field(&self, field_name: &str) -> Result; fn oneof_group(&self, field_name: &str) -> Result>; + fn py_identifier(&self) -> u64; } impl PyAnyExtras for PyAny { @@ -49,4 +50,19 @@ impl PyAnyExtras for PyAny { .extract()?; Ok(opt) } + + fn py_identifier(&self) -> u64 { + static FUN_CACHE: GILOnceCell> = GILOnceCell::new(); + let py = self.py(); + let fun = FUN_CACHE.get_or_init(py, || { + let fun = py + .eval("id", None, None) + .expect("This is a valid Python expression"); + Py::from(fun) + }); + fun.call1(py, (self,)) + .expect("Identity function is callable") + .extract::(py) + .expect("Identity function always returns an integer") + } }