From 682f19654d0ec79ac769979df5e1de6525faa8d7 Mon Sep 17 00:00:00 2001 From: Vitor Hideyoshi Date: Mon, 24 Nov 2025 20:52:02 -0300 Subject: [PATCH] feat: better methodology for accessing cached references of: objects, subobjects and defs --- jambo/parser/anyof_type_parser.py | 2 +- jambo/parser/object_type_parser.py | 13 +++---- tests/test_schema_converter.py | 56 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/jambo/parser/anyof_type_parser.py b/jambo/parser/anyof_type_parser.py index 9b09754..c0295ff 100644 --- a/jambo/parser/anyof_type_parser.py +++ b/jambo/parser/anyof_type_parser.py @@ -31,7 +31,7 @@ class AnyOfTypeParser(GenericTypeParser): sub_types = [ GenericTypeParser.type_from_properties( - f"{name}_sub{i}", subProperty, **kwargs + f"{name}.sub{i}", subProperty, **kwargs ) for i, subProperty in enumerate(sub_properties) ] diff --git a/jambo/parser/object_type_parser.py b/jambo/parser/object_type_parser.py index fc10cb5..475c785 100644 --- a/jambo/parser/object_type_parser.py +++ b/jambo/parser/object_type_parser.py @@ -67,7 +67,7 @@ class ObjectTypeParser(GenericTypeParser): return model model_config = ConfigDict(validate_assignment=True) - fields = cls._parse_properties(properties, required_keys, **kwargs) + fields = cls._parse_properties(name, properties, required_keys, **kwargs) model = create_model(name, __config__=model_config, **fields) # type: ignore @@ -84,6 +84,7 @@ class ObjectTypeParser(GenericTypeParser): @classmethod def _parse_properties( cls, + name: str, properties: dict[str, JSONSchema], required_keys: list[str], **kwargs: Unpack[TypeParserOptions], @@ -91,15 +92,15 @@ class ObjectTypeParser(GenericTypeParser): required_keys = required_keys or [] fields = {} - for name, prop in properties.items(): + for field_name, field_prop in properties.items(): sub_property: TypeParserOptions = kwargs.copy() - sub_property["required"] = name in required_keys + sub_property["required"] = field_name in required_keys parsed_type, parsed_properties = GenericTypeParser.type_from_properties( - name, - prop, + f"{name}.{field_name}", + field_prop, **sub_property, # type: ignore ) - fields[name] = (parsed_type, Field(**parsed_properties)) + fields[field_name] = (parsed_type, Field(**parsed_properties)) return fields diff --git a/tests/test_schema_converter.py b/tests/test_schema_converter.py index 6cd66ef..245ca8e 100644 --- a/tests/test_schema_converter.py +++ b/tests/test_schema_converter.py @@ -942,3 +942,59 @@ class TestSchemaConverter(TestCase): cached_model = self.converter.get_cached_ref("NonExistentModel") self.assertIsNone(cached_model) + + def test_get_type_from_cache_nested_type(self): + schema = { + "title": "Person", + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "integer"}, + "address": { + "type": "object", + "properties": { + "street": {"type": "string"}, + "city": {"type": "string"}, + }, + "required": ["street", "city"], + }, + }, + "required": ["name", "age", "address"], + } + + model = self.converter.build_with_instance(schema) + + cached_model = self.converter.get_cached_ref("Person.address") + + self.assertIsNotNone(cached_model) + self.assertIs(model.model_fields["address"].annotation, cached_model) + + def test_get_type_from_cache_with_def(self): + schema = { + "title": "person", + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "integer"}, + "address": {"$ref": "#/$defs/address"}, + }, + "$defs": { + "address": { + "type": "object", + "properties": { + "street": {"type": "string"}, + "city": {"type": "string"}, + }, + "required": ["street", "city"], + } + }, + } + + person_model = self.converter.build_with_instance(schema) + cached_person_model = self.converter.get_cached_ref("person") + + self.assertIs(person_model, cached_person_model) + + cached_address_model = self.converter.get_cached_ref("address") + + self.assertIsNotNone(cached_address_model)