diff --git a/jambo/parser/_type_parser.py b/jambo/parser/_type_parser.py index 024fed3..20a11d4 100644 --- a/jambo/parser/_type_parser.py +++ b/jambo/parser/_type_parser.py @@ -13,11 +13,12 @@ class GenericTypeParser(ABC, Generic[T]): json_schema_type: str = None - @staticmethod - @abstractmethod - def from_properties( - name: str, properties: dict[str, any], required: bool = False - ) -> tuple[T, dict]: ... + default_mappings = { + "default": "default", + "description": "description", + } + + type_mappings: dict[str, str] = None @classmethod def get_impl(cls, type_name: str) -> Self: @@ -30,7 +31,24 @@ class GenericTypeParser(ABC, Generic[T]): raise ValueError(f"Unknown type: {type_name}") - @staticmethod - def validate_default(field_type: type, field_prop: dict, value): + @abstractmethod + def from_properties( + self, name: str, properties: dict[str, any], required: bool = False + ) -> tuple[T, dict]: ... + + def mappings_properties_builder(self, properties, required=False) -> dict[str, any]: + if self.type_mappings is None: + raise NotImplementedError("Type mappings not defined") + + if not required: + properties["default"] = properties.get("default", None) + + mappings = self.default_mappings | self.type_mappings + + return { + mappings[key]: value for key, value in properties.items() if key in mappings + } + + def validate_default(self, field_type: type, field_prop: dict, value) -> None: field = Annotated[field_type, Field(**field_prop)] TypeAdapter(field).validate_python(value) diff --git a/jambo/parser/allof_type_parser.py b/jambo/parser/allof_type_parser.py index 7a65b49..f0b1839 100644 --- a/jambo/parser/allof_type_parser.py +++ b/jambo/parser/allof_type_parser.py @@ -6,8 +6,7 @@ class AllOfTypeParser(GenericTypeParser): json_schema_type = "allOf" - @staticmethod - def from_properties(name, properties, required=False): + def from_properties(self, name, properties, required=False): subProperties = properties.get("allOf") if not subProperties: raise ValueError("Invalid JSON Schema: 'allOf' is not specified.") @@ -28,16 +27,13 @@ class AllOfTypeParser(GenericTypeParser): # If a sub-property has not defined a type, we need to set it to the top-level type subProperty["type"] = _mapped_type - combined_properties = AllOfTypeParser._rebuild_properties_from_subproperties( - subProperties - ) + combined_properties = self._rebuild_properties_from_subproperties(subProperties) return GenericTypeParser.get_impl(_mapped_type).from_properties( name, combined_properties ) - @staticmethod - def _rebuild_properties_from_subproperties(subProperties): + def _rebuild_properties_from_subproperties(self, subProperties): properties = {} for subProperty in subProperties: for name, prop in subProperty.items(): diff --git a/jambo/parser/anyof_type_parser.py b/jambo/parser/anyof_type_parser.py index 76b1a32..b70591e 100644 --- a/jambo/parser/anyof_type_parser.py +++ b/jambo/parser/anyof_type_parser.py @@ -11,8 +11,7 @@ class AnyOfTypeParser(GenericTypeParser): json_schema_type = "anyOf" - @staticmethod - def from_properties(name, properties, required=False): + def from_properties(self, name, properties, required=False): if "anyOf" not in properties: raise ValueError(f"Invalid JSON Schema: {properties}") @@ -34,9 +33,7 @@ class AnyOfTypeParser(GenericTypeParser): if default_value is not None: for sub_type, sub_property in sub_types: try: - GenericTypeParser.validate_default( - sub_type, sub_property, default_value - ) + self.validate_default(sub_type, sub_property, default_value) break except ValueError: continue diff --git a/jambo/parser/array_type_parser.py b/jambo/parser/array_type_parser.py index b72c3ed..5ec162c 100644 --- a/jambo/parser/array_type_parser.py +++ b/jambo/parser/array_type_parser.py @@ -1,7 +1,4 @@ from jambo.parser._type_parser import GenericTypeParser -from jambo.utils.properties_builder.mappings_properties_builder import ( - mappings_properties_builder, -) import copy from typing import TypeVar @@ -15,30 +12,29 @@ class ArrayTypeParser(GenericTypeParser): json_schema_type = "array" - @staticmethod - def from_properties(name, properties, required=False): + default_mappings = {"description": "description"} + + type_mappings = { + "maxItems": "max_length", + "minItems": "min_length", + } + + def from_properties(self, name, properties, required=False): _item_type, _item_args = GenericTypeParser.get_impl( properties["items"]["type"] ).from_properties(name, properties["items"], required=True) - _mappings = { - "maxItems": "max_length", - "minItems": "min_length", - } - wrapper_type = set if properties.get("uniqueItems", False) else list field_type = wrapper_type[_item_type] - mapped_properties = mappings_properties_builder( + mapped_properties = self.mappings_properties_builder( properties, - _mappings, required=required, - default_mappings={"description": "description"}, ) default_list = properties.pop("default", None) if default_list is not None: - ArrayTypeParser.validate_default( + self.validate_default( field_type, mapped_properties, default_list, diff --git a/jambo/parser/boolean_type_parser.py b/jambo/parser/boolean_type_parser.py index e9f0ab0..384da9d 100644 --- a/jambo/parser/boolean_type_parser.py +++ b/jambo/parser/boolean_type_parser.py @@ -1,7 +1,4 @@ from jambo.parser._type_parser import GenericTypeParser -from jambo.utils.properties_builder.mappings_properties_builder import ( - mappings_properties_builder, -) class BooleanTypeParser(GenericTypeParser): @@ -9,13 +6,12 @@ class BooleanTypeParser(GenericTypeParser): json_schema_type = "boolean" - @staticmethod - def from_properties(name, properties, required=False): - _mappings = { - "default": "default", - } + type_mappings = { + "default": "default", + } - mapped_properties = mappings_properties_builder(properties, _mappings, required) + def from_properties(self, name, properties, required=False): + mapped_properties = self.mappings_properties_builder(properties, required) 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 4e1b075..565f69e 100644 --- a/jambo/parser/float_type_parser.py +++ b/jambo/parser/float_type_parser.py @@ -1,7 +1,4 @@ from jambo.parser._type_parser import GenericTypeParser -from jambo.utils.properties_builder.mappings_properties_builder import ( - mappings_properties_builder, -) class FloatTypeParser(GenericTypeParser): @@ -9,20 +6,20 @@ class FloatTypeParser(GenericTypeParser): json_schema_type = "number" - @staticmethod - def from_properties(name, properties, required=False): - _mappings = { - "minimum": "ge", - "exclusiveMinimum": "gt", - "maximum": "le", - "exclusiveMaximum": "lt", - "multipleOf": "multiple_of", - "default": "default", - } - mapped_properties = mappings_properties_builder(properties, _mappings, required) + type_mappings = { + "minimum": "ge", + "exclusiveMinimum": "gt", + "maximum": "le", + "exclusiveMaximum": "lt", + "multipleOf": "multiple_of", + "default": "default", + } + + def from_properties(self, name, properties, required=False): + mapped_properties = self.mappings_properties_builder(properties, required) default_value = mapped_properties.get("default") if default_value is not None: - FloatTypeParser.validate_default(float, mapped_properties, default_value) + self.validate_default(float, mapped_properties, default_value) return float, mapped_properties diff --git a/jambo/parser/int_type_parser.py b/jambo/parser/int_type_parser.py index 3e0f92e..2a352bb 100644 --- a/jambo/parser/int_type_parser.py +++ b/jambo/parser/int_type_parser.py @@ -1,7 +1,4 @@ from jambo.parser._type_parser import GenericTypeParser -from jambo.utils.properties_builder.mappings_properties_builder import ( - mappings_properties_builder, -) class IntTypeParser(GenericTypeParser): @@ -9,20 +6,20 @@ class IntTypeParser(GenericTypeParser): json_schema_type = "integer" - @staticmethod - def from_properties(name, properties, required=False): - _mappings = { - "minimum": "ge", - "exclusiveMinimum": "gt", - "maximum": "le", - "exclusiveMaximum": "lt", - "multipleOf": "multiple_of", - "default": "default", - } - mapped_properties = mappings_properties_builder(properties, _mappings, required) + type_mappings = { + "minimum": "ge", + "exclusiveMinimum": "gt", + "maximum": "le", + "exclusiveMaximum": "lt", + "multipleOf": "multiple_of", + "default": "default", + } + + def from_properties(self, name, properties, required=False): + mapped_properties = self.mappings_properties_builder(properties, required) default_value = mapped_properties.get("default") if default_value is not None: - IntTypeParser.validate_default(int, mapped_properties, default_value) + self.validate_default(int, mapped_properties, default_value) return int, mapped_properties diff --git a/jambo/parser/string_type_parser.py b/jambo/parser/string_type_parser.py index 7b77c75..89e8b7e 100644 --- a/jambo/parser/string_type_parser.py +++ b/jambo/parser/string_type_parser.py @@ -1,7 +1,4 @@ from jambo.parser._type_parser import GenericTypeParser -from jambo.utils.properties_builder.mappings_properties_builder import ( - mappings_properties_builder, -) class StringTypeParser(GenericTypeParser): @@ -9,18 +6,17 @@ class StringTypeParser(GenericTypeParser): json_schema_type = "string" - @staticmethod - def from_properties(name, properties, required=False): - _mappings = { - "maxLength": "max_length", - "minLength": "min_length", - "pattern": "pattern", - } + type_mappings = { + "maxLength": "max_length", + "minLength": "min_length", + "pattern": "pattern", + } - mapped_properties = mappings_properties_builder(properties, _mappings, required) + def from_properties(self, name, properties, required=False): + mapped_properties = self.mappings_properties_builder(properties, required) default_value = properties.get("default") if default_value is not None: - StringTypeParser.validate_default(str, mapped_properties, default_value) + self.validate_default(str, mapped_properties, default_value) return str, mapped_properties diff --git a/jambo/utils/__init__.py b/jambo/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jambo/utils/properties_builder/__init__.py b/jambo/utils/properties_builder/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/jambo/utils/properties_builder/mappings_properties_builder.py b/jambo/utils/properties_builder/mappings_properties_builder.py deleted file mode 100644 index e78fc8e..0000000 --- a/jambo/utils/properties_builder/mappings_properties_builder.py +++ /dev/null @@ -1,16 +0,0 @@ -def mappings_properties_builder( - properties, mappings, required=False, default_mappings=None -): - if not required: - properties["default"] = properties.get("default", None) - - default_mappings = default_mappings or { - "default": "default", - "description": "description", - } - - mappings = default_mappings | mappings - - return { - mappings[key]: value for key, value in properties.items() if key in mappings - } diff --git a/jambo/utils/properties_builder/numeric_properties_builder.py b/jambo/utils/properties_builder/numeric_properties_builder.py deleted file mode 100644 index 343ef4c..0000000 --- a/jambo/utils/properties_builder/numeric_properties_builder.py +++ /dev/null @@ -1,52 +0,0 @@ -from jambo.utils.properties_builder.mappings_properties_builder import ( - mappings_properties_builder, -) - - -def numeric_properties_builder(properties, required=False): - _mappings = { - "minimum": "ge", - "exclusiveMinimum": "gt", - "maximum": "le", - "exclusiveMaximum": "lt", - "multipleOf": "multiple_of", - "default": "default", - } - - mapped_properties = mappings_properties_builder(properties, _mappings, required) - - default_value = properties.get("default") - if default_value is not None: - default_value = properties["default"] - if not isinstance(default_value, (int, float)): - raise ValueError( - f"Default value must be a number, got {type(default_value).__name__}" - ) - - if default_value > properties.get("maximum", float("inf")): - raise ValueError( - f"Default value exceeds maximum limit of {properties.get('maximum')}" - ) - - if default_value < properties.get("minimum", float("-inf")): - raise ValueError( - f"Default value is below minimum limit of {properties.get('minimum')}" - ) - - if default_value >= properties.get("exclusiveMaximum", float("inf")): - raise ValueError( - f"Default value exceeds exclusive maximum limit of {properties.get('exclusiveMaximum')}" - ) - - if default_value <= properties.get("exclusiveMinimum", float("-inf")): - raise ValueError( - f"Default value is below exclusive minimum limit of {properties.get('exclusiveMinimum')}" - ) - - if "multipleOf" in properties: - if default_value % properties["multipleOf"] != 0: - raise ValueError( - f"Default value {default_value} is not a multiple of {properties['multipleOf']}" - ) - - return mapped_properties diff --git a/jambo/utils/types/__init__.py b/jambo/utils/types/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/parser/test_allof_type_parser.py b/tests/parser/test_allof_type_parser.py index 95ce68e..e35319c 100644 --- a/tests/parser/test_allof_type_parser.py +++ b/tests/parser/test_allof_type_parser.py @@ -38,7 +38,7 @@ class TestAllOfTypeParser(TestCase): ], } - type_parsing, type_validator = AllOfTypeParser.from_properties( + type_parsing, type_validator = AllOfTypeParser().from_properties( "placeholder", properties ) @@ -83,7 +83,7 @@ class TestAllOfTypeParser(TestCase): ], } - type_parsing, type_validator = AllOfTypeParser.from_properties( + type_parsing, type_validator = AllOfTypeParser().from_properties( "placeholder", properties ) @@ -112,7 +112,7 @@ class TestAllOfTypeParser(TestCase): ], } - type_parsing, type_validator = AllOfTypeParser.from_properties( + type_parsing, type_validator = AllOfTypeParser().from_properties( "placeholder", properties ) @@ -133,7 +133,7 @@ class TestAllOfTypeParser(TestCase): ] } - type_parsing, type_validator = AllOfTypeParser.from_properties( + type_parsing, type_validator = AllOfTypeParser().from_properties( "placeholder", properties ) @@ -155,7 +155,7 @@ class TestAllOfTypeParser(TestCase): } with self.assertRaises(ValueError): - AllOfTypeParser.from_properties("placeholder", properties) + AllOfTypeParser().from_properties("placeholder", properties) def test_all_of_invalid_type_not_present(self): properties = { @@ -168,7 +168,7 @@ class TestAllOfTypeParser(TestCase): } with self.assertRaises(ValueError): - AllOfTypeParser.from_properties("placeholder", properties) + AllOfTypeParser().from_properties("placeholder", properties) def test_all_of_invalid_type_in_fields(self): properties = { @@ -181,7 +181,7 @@ class TestAllOfTypeParser(TestCase): } with self.assertRaises(ValueError): - AllOfTypeParser.from_properties("placeholder", properties) + AllOfTypeParser().from_properties("placeholder", properties) def test_all_of_description_field(self): """ @@ -218,7 +218,7 @@ class TestAllOfTypeParser(TestCase): ], } - type_parsing, _ = AllOfTypeParser.from_properties("placeholder", properties) + type_parsing, _ = AllOfTypeParser().from_properties("placeholder", properties) self.assertEqual( type_parsing.schema()["properties"]["name"]["description"], @@ -256,7 +256,7 @@ class TestAllOfTypeParser(TestCase): ], } - type_parsing, _ = AllOfTypeParser.from_properties("placeholder", properties) + type_parsing, _ = AllOfTypeParser().from_properties("placeholder", properties) obj = type_parsing() self.assertEqual(obj.name, "John") self.assertEqual(obj.age, 30) @@ -289,4 +289,4 @@ class TestAllOfTypeParser(TestCase): } with self.assertRaises(ValueError): - AllOfTypeParser.from_properties("placeholder", properties) + AllOfTypeParser().from_properties("placeholder", properties) diff --git a/tests/parser/test_anyof_type_parser.py b/tests/parser/test_anyof_type_parser.py index 896b394..d16a3c1 100644 --- a/tests/parser/test_anyof_type_parser.py +++ b/tests/parser/test_anyof_type_parser.py @@ -19,7 +19,7 @@ class TestAnyOfTypeParser(TestCase): ], } - type_parsing, _ = AnyOfTypeParser.from_properties("placeholder", properties) + type_parsing, _ = AnyOfTypeParser().from_properties("placeholder", properties) # check union type has string and int self.assertEqual(get_origin(type_parsing), Union) @@ -45,7 +45,7 @@ class TestAnyOfTypeParser(TestCase): "default": 42, } - type_parsing, type_validator = AnyOfTypeParser.from_properties( + type_parsing, type_validator = AnyOfTypeParser().from_properties( "placeholder", properties )