Implements Object Defaults #10

Merged
HideyoshiNakazone merged 1 commits from implements-object-default into main 2025-04-13 05:48:42 +00:00
7 changed files with 71 additions and 15 deletions

View File

@@ -1,5 +1,7 @@
from jambo.parser._type_parser import GenericTypeParser from jambo.parser._type_parser import GenericTypeParser
from jambo.utils.properties_builder.numeric_properties_builder import numeric_properties_builder from jambo.utils.properties_builder.numeric_properties_builder import (
numeric_properties_builder,
)
class FloatTypeParser(GenericTypeParser): class FloatTypeParser(GenericTypeParser):

View File

@@ -1,5 +1,7 @@
from jambo.parser._type_parser import GenericTypeParser from jambo.parser._type_parser import GenericTypeParser
from jambo.utils.properties_builder.numeric_properties_builder import numeric_properties_builder from jambo.utils.properties_builder.numeric_properties_builder import (
numeric_properties_builder,
)
class IntTypeParser(GenericTypeParser): class IntTypeParser(GenericTypeParser):

View File

@@ -10,10 +10,12 @@ class ObjectTypeParser(GenericTypeParser):
def from_properties(name, properties): def from_properties(name, properties):
from jambo.schema_converter import SchemaConverter from jambo.schema_converter import SchemaConverter
if "default" in properties: type_parsing = SchemaConverter.build_object(name, properties)
raise RuntimeError("Default values for objects are not supported.") type_properties = {}
return ( if "default" in properties:
SchemaConverter.build_object(name, properties), type_properties["default_factory"] = lambda: type_parsing.model_validate(
{}, # The second argument is not used in this case properties["default"]
) )
return type_parsing, type_properties

View File

@@ -1,13 +1,11 @@
from jambo.parser import GenericTypeParser from jambo.parser import GenericTypeParser
from jambo.types.json_schema_type import JSONSchema
from jsonschema.exceptions import SchemaError from jsonschema.exceptions import SchemaError
from jsonschema.protocols import Validator from jsonschema.protocols import Validator
from pydantic import create_model from pydantic import create_model
from pydantic.fields import Field from pydantic.fields import Field
from pydantic.main import ModelT
from typing import Type
from jambo.types.json_schema_type import JSONSchema
class SchemaConverter: class SchemaConverter:
@@ -20,7 +18,7 @@ class SchemaConverter:
""" """
@staticmethod @staticmethod
def build(schema: JSONSchema) -> Type: def build(schema: JSONSchema) -> ModelT:
""" """
Converts a JSON Schema to a Pydantic model. Converts a JSON Schema to a Pydantic model.
:param schema: The JSON Schema to convert. :param schema: The JSON Schema to convert.
@@ -35,7 +33,7 @@ class SchemaConverter:
def build_object( def build_object(
name: str, name: str,
schema: JSONSchema, schema: JSONSchema,
) -> Type: ) -> ModelT:
""" """
Converts a JSON Schema object to a Pydantic model given a name. Converts a JSON Schema object to a Pydantic model given a name.
:param name: :param name:
@@ -60,7 +58,7 @@ class SchemaConverter:
@staticmethod @staticmethod
def _build_model_from_properties( def _build_model_from_properties(
model_name: str, model_properties: dict, required_keys: list[str] model_name: str, model_properties: dict, required_keys: list[str]
) -> Type: ) -> ModelT:
properties = SchemaConverter._parse_properties(model_properties, required_keys) properties = SchemaConverter._parse_properties(model_properties, required_keys)
return create_model(model_name, **properties) return create_model(model_name, **properties)

View File

@@ -66,3 +66,4 @@ section-order=[
"third-party", "third-party",
"standard-library", "standard-library",
] ]
lines-after-imports = 2

View File

@@ -21,3 +21,29 @@ class TestObjectTypeParser(TestCase):
self.assertEqual(obj.name, "name") self.assertEqual(obj.name, "name")
self.assertEqual(obj.age, 10) self.assertEqual(obj.age, 10)
def test_object_type_parser_with_default(self):
parser = ObjectTypeParser()
properties = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"default": {
"name": "default_name",
"age": 20,
},
}
_, type_validator = parser.from_properties("placeholder", properties)
# Check default value
default_obj = type_validator["default_factory"]()
self.assertEqual(default_obj.name, "default_name")
self.assertEqual(default_obj.age, 20)
# Chekc default factory new object id
new_obj = type_validator["default_factory"]()
self.assertNotEqual(id(default_obj), id(new_obj))

View File

@@ -256,3 +256,28 @@ class TestSchemaConverter(TestCase):
model_set = SchemaConverter.build(schema_set) model_set = SchemaConverter.build(schema_set)
self.assertEqual(model_set().friends, {"John", "Jane"}) self.assertEqual(model_set().friends, {"John", "Jane"})
def test_default_for_object(self):
schema = {
"title": "Person",
"description": "A person",
"type": "object",
"properties": {
"address": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"},
},
"default": {"street": "123 Main St", "city": "Springfield"},
},
},
"required": ["address"],
}
model = SchemaConverter.build(schema)
obj = model(address={"street": "123 Main St", "city": "Springfield"})
self.assertEqual(obj.address.street, "123 Main St")
self.assertEqual(obj.address.city, "Springfield")