Initial Working $ref Keyword with: ForwardRef, Partial Root Ref and Recursive Ref

This commit is contained in:
2025-06-13 01:36:16 -03:00
parent 188cd28586
commit f4effac41c
7 changed files with 234 additions and 63 deletions

View File

@@ -1,7 +1,5 @@
from jambo.parser import ObjectTypeParser, RefTypeParser
from typing_extensions import ForwardRef, get_type_hints
from unittest import TestCase
@@ -25,6 +23,7 @@ class TestRefTypeParser(TestCase):
"person",
properties,
context=properties,
ref_cache={},
required=True,
)
@@ -46,38 +45,101 @@ class TestRefTypeParser(TestCase):
"$ref": "#",
},
},
"required": ["name", "age"],
}
type_parsing, type_validator = ObjectTypeParser().from_properties(
model, type_validator = ObjectTypeParser().from_properties(
"person",
properties,
context=properties,
ref_cache={},
required=True,
)
type_parsing.update_forward_refs(person=type_parsing)
self.assertIsInstance(type_parsing, type)
obj = model(
name="John",
age=30,
emergency_contact=model(
name="Jane",
age=28,
),
)
type_hints = get_type_hints(type_parsing, globals(), locals())
self.assertEqual(obj.name, "John")
self.assertEqual(obj.age, 30)
self.assertIsInstance(obj.emergency_contact, model)
self.assertEqual(obj.emergency_contact.name, "Jane")
self.assertEqual(obj.emergency_contact.age, 28)
self.assertIsInstance(type_hints["emergency_contact"], ForwardRef)
def test_ref_type_parser_forward_ref_can_checks_validation(self):
properties = {
"title": "person",
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"emergency_contact": {
"$ref": "#",
},
},
"required": ["name", "age"],
}
"""
This is a example of how to resolve ForwardRef in a dynamic model:
```python
from typing import get_type_hints
# Make sure your dynamic model has a name
model = type_parsing
model.update_forward_refs(person=model) # 👈 resolve the ForwardRef("person")
# Inject into globals manually
globalns = globals().copy()
globalns['person'] = model
# Now you can get the resolved hints
type_hints = get_type_hints(model, globalns=globalns)
```
Use `TypeParserOptions.ref_cache` option to cache and resolve ForwardRefs
inside the ObjectTypeParser.to_model method.
"""
model, type_validator = ObjectTypeParser().from_properties(
"person",
properties,
context=properties,
ref_cache={},
required=True,
)
# checks if when created via FowardRef the model is validated correctly.
with self.assertRaises(ValueError):
model(
name="John",
age=30,
emergency_contact=model(
name="Jane",
),
)
def test_ref_type_parser_with_ciclic_def(self):
properties = {
"title": "person",
"$ref": "#/$defs/person",
"$defs": {
"person": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"emergency_contact": {
"$ref": "#/$defs/person",
},
},
}
},
}
model, type_validator = RefTypeParser().from_properties(
"person",
properties,
context=properties,
ref_cache={},
required=True,
)
obj = model(
name="John",
age=30,
emergency_contact=model(
name="Jane",
age=28,
),
)
self.assertEqual(obj.name, "John")
self.assertEqual(obj.age, 30)
self.assertIsInstance(obj.emergency_contact, model)
self.assertEqual(obj.emergency_contact.name, "Jane")
self.assertEqual(obj.emergency_contact.age, 28)