From be7f04e20d59337a6b7c4c597b14c3e7dd200dbc Mon Sep 17 00:00:00 2001 From: Vitor Hideyoshi Date: Tue, 3 Jun 2025 02:05:21 -0300 Subject: [PATCH] Better TypeParser Kwargs --- jambo/parser/_type_parser.py | 8 +++++--- jambo/parser/array_type_parser.py | 14 +++++++------- jambo/parser/boolean_type_parser.py | 7 +++++-- jambo/parser/float_type_parser.py | 7 +++++-- jambo/parser/int_type_parser.py | 7 +++++-- jambo/parser/object_type_parser.py | 26 +++++++++++++++++++------- jambo/parser/string_type_parser.py | 6 ++++-- 7 files changed, 50 insertions(+), 25 deletions(-) diff --git a/jambo/parser/_type_parser.py b/jambo/parser/_type_parser.py index b1a7591..66c6245 100644 --- a/jambo/parser/_type_parser.py +++ b/jambo/parser/_type_parser.py @@ -48,7 +48,9 @@ class GenericTypeParser(ABC, Generic[T]): @classmethod def _get_schema_type(cls) -> tuple[str, str | None]: if cls.json_schema_type is None: - raise RuntimeError("TypeParser: json_schema_type not defined") + raise RuntimeError( + f"TypeParser: json_schema_type not defined for subclass {cls.__name__}" + ) schema_definition = cls.json_schema_type.split(":") @@ -58,12 +60,12 @@ class GenericTypeParser(ABC, Generic[T]): return schema_definition[0], schema_definition[1] def mappings_properties_builder( - self, properties, required=False, **kwargs: Unpack[TypeParserOptions] + self, properties, **kwargs: Unpack[TypeParserOptions] ) -> dict[str, Any]: if self.type_mappings is None: raise NotImplementedError("Type mappings not defined") - if not required: + if not kwargs.get("required", False): properties["default"] = properties.get("default", None) mappings = self.default_mappings | self.type_mappings diff --git a/jambo/parser/array_type_parser.py b/jambo/parser/array_type_parser.py index d933c66..612044e 100644 --- a/jambo/parser/array_type_parser.py +++ b/jambo/parser/array_type_parser.py @@ -1,6 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser +from jambo.types.type_parser_options import TypeParserOptions -from typing_extensions import TypeVar +from typing_extensions import TypeVar, Unpack import copy @@ -20,18 +21,17 @@ class ArrayTypeParser(GenericTypeParser): "minItems": "min_length", } - def from_properties(self, name, properties, required=False): + def from_properties(self, name, properties, **kwargs: Unpack[TypeParserOptions]): + item_properties = kwargs.copy() + item_properties["required"] = True _item_type, _item_args = GenericTypeParser.type_from_properties( - name, properties["items"], required=True + name, properties["items"], **item_properties ) wrapper_type = set if properties.get("uniqueItems", False) else list field_type = wrapper_type[_item_type] - mapped_properties = self.mappings_properties_builder( - properties, - required=required, - ) + mapped_properties = self.mappings_properties_builder(properties, **kwargs) default_list = properties.pop("default", None) if default_list is not None: diff --git a/jambo/parser/boolean_type_parser.py b/jambo/parser/boolean_type_parser.py index f2ae257..b2fc1b0 100644 --- a/jambo/parser/boolean_type_parser.py +++ b/jambo/parser/boolean_type_parser.py @@ -1,4 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser +from jambo.types.type_parser_options import TypeParserOptions + +from typing_extensions import Unpack class BooleanTypeParser(GenericTypeParser): @@ -10,8 +13,8 @@ class BooleanTypeParser(GenericTypeParser): "default": "default", } - def from_properties(self, name, properties, required=False): - mapped_properties = self.mappings_properties_builder(properties, required) + def from_properties(self, name, properties, **kwargs: Unpack[TypeParserOptions]): + mapped_properties = self.mappings_properties_builder(properties, **kwargs) default_value = properties.get("default") if default_value is not None and not isinstance(default_value, bool): diff --git a/jambo/parser/float_type_parser.py b/jambo/parser/float_type_parser.py index f5ab7e3..f682cf8 100644 --- a/jambo/parser/float_type_parser.py +++ b/jambo/parser/float_type_parser.py @@ -1,4 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser +from jambo.types.type_parser_options import TypeParserOptions + +from typing_extensions import Unpack class FloatTypeParser(GenericTypeParser): @@ -15,8 +18,8 @@ class FloatTypeParser(GenericTypeParser): "default": "default", } - def from_properties(self, name, properties, required=False): - mapped_properties = self.mappings_properties_builder(properties, required) + def from_properties(self, name, properties, **kwargs: Unpack[TypeParserOptions]): + mapped_properties = self.mappings_properties_builder(properties, **kwargs) default_value = mapped_properties.get("default") if default_value is not None: diff --git a/jambo/parser/int_type_parser.py b/jambo/parser/int_type_parser.py index 7041047..e65ecda 100644 --- a/jambo/parser/int_type_parser.py +++ b/jambo/parser/int_type_parser.py @@ -1,4 +1,7 @@ from jambo.parser._type_parser import GenericTypeParser +from jambo.types.type_parser_options import TypeParserOptions + +from typing_extensions import Unpack class IntTypeParser(GenericTypeParser): @@ -15,8 +18,8 @@ class IntTypeParser(GenericTypeParser): "default": "default", } - def from_properties(self, name, properties, required=False): - mapped_properties = self.mappings_properties_builder(properties, required) + def from_properties(self, name, properties, **kwargs: Unpack[TypeParserOptions]): + mapped_properties = self.mappings_properties_builder(properties, **kwargs) default_value = mapped_properties.get("default") if default_value is not None: diff --git a/jambo/parser/object_type_parser.py b/jambo/parser/object_type_parser.py index b10f454..72546d9 100644 --- a/jambo/parser/object_type_parser.py +++ b/jambo/parser/object_type_parser.py @@ -1,8 +1,9 @@ from jambo.parser._type_parser import GenericTypeParser +from jambo.types.type_parser_options import TypeParserOptions from pydantic import Field, create_model from pydantic.main import ModelT -from typing_extensions import Any +from typing_extensions import Any, Unpack class ObjectTypeParser(GenericTypeParser): @@ -11,10 +12,13 @@ class ObjectTypeParser(GenericTypeParser): json_schema_type = "type:object" def from_properties( - self, name: str, properties: dict[str, Any], required: bool = False + self, name: str, properties: dict[str, Any], **kwargs: Unpack[TypeParserOptions] ): type_parsing = self.to_model( - name, properties.get("properties", {}), properties.get("required", []) + name, + properties.get("properties", {}), + properties.get("required", []), + **kwargs, ) type_properties = {} @@ -26,7 +30,11 @@ class ObjectTypeParser(GenericTypeParser): return type_parsing, type_properties def to_model( - self, name: str, schema: dict[str, Any], required_keys: list[str], **kwargs + self, + name: str, + schema: dict[str, Any], + required_keys: list[str], + **kwargs: Unpack[TypeParserOptions], ) -> type[ModelT]: """ Converts JSON Schema object properties to a Pydantic model. @@ -40,15 +48,19 @@ class ObjectTypeParser(GenericTypeParser): @staticmethod def _parse_properties( - properties: dict[str, Any], required_keys: list[str], **kwargs + properties: dict[str, Any], + required_keys: list[str], + **kwargs: Unpack[TypeParserOptions], ) -> dict[str, tuple[type, Field]]: required_keys = required_keys or [] fields = {} for name, prop in properties.items(): - is_required = name in required_keys + sub_property = kwargs.copy() + sub_property["required"] = name in required_keys + parsed_type, parsed_properties = GenericTypeParser.type_from_properties( - name, prop, required=is_required, **kwargs + name, prop, **sub_property ) fields[name] = (parsed_type, Field(**parsed_properties)) diff --git a/jambo/parser/string_type_parser.py b/jambo/parser/string_type_parser.py index e0aa02b..fe4df68 100644 --- a/jambo/parser/string_type_parser.py +++ b/jambo/parser/string_type_parser.py @@ -1,6 +1,8 @@ from jambo.parser._type_parser import GenericTypeParser +from jambo.types.type_parser_options import TypeParserOptions from pydantic import EmailStr, HttpUrl, IPvAnyAddress +from typing_extensions import Unpack from datetime import date, datetime, time @@ -32,8 +34,8 @@ class StringTypeParser(GenericTypeParser): "hostname": r"^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$", } - def from_properties(self, name, properties, required=False): - mapped_properties = self.mappings_properties_builder(properties, required) + def from_properties(self, name, properties, **kwargs: Unpack[TypeParserOptions]): + mapped_properties = self.mappings_properties_builder(properties, **kwargs) format_type = properties.get("format") if format_type: