From d5149061a142d39e1a4b039285f851e2f65b78a9 Mon Sep 17 00:00:00 2001 From: Vitor Hideyoshi Date: Thu, 17 Apr 2025 03:04:38 -0300 Subject: [PATCH] Formats Import Orders --- jambo/parser/__init__.py | 8 +- jambo/parser/_type_parser.py | 7 +- jambo/parser/array_type_parser.py | 9 +- jambo/types/json_schema_type.py | 2 +- pyproject.toml | 2 +- tests/parser/test_allof_type_parser.py | 288 ++++++++++++++++++++++++ tests/parser/test_array_type_parser.py | 4 +- tests/parser/test_bool_type_parser.py | 4 +- tests/parser/test_float_type_parser.py | 4 +- tests/parser/test_int_type_parser.py | 4 +- tests/parser/test_object_type_parser.py | 4 +- tests/parser/test_string_type_parser.py | 4 +- 12 files changed, 314 insertions(+), 26 deletions(-) create mode 100644 tests/parser/test_allof_type_parser.py diff --git a/jambo/parser/__init__.py b/jambo/parser/__init__.py index d4a71e3..b8af117 100644 --- a/jambo/parser/__init__.py +++ b/jambo/parser/__init__.py @@ -1,11 +1,11 @@ # Exports generic type parser from ._type_parser import GenericTypeParser as GenericTypeParser +from .allof_type_parser import AllOfTypeParser as AllOfTypeParser +from .array_type_parser import ArrayTypeParser as ArrayTypeParser +from .boolean_type_parser import BooleanTypeParser as BooleanTypeParser +from .float_type_parser import FloatTypeParser as FloatTypeParser # Exports Implementations from .int_type_parser import IntTypeParser as IntTypeParser from .object_type_parser import ObjectTypeParser as ObjectTypeParser from .string_type_parser import StringTypeParser as StringTypeParser -from .array_type_parser import ArrayTypeParser as ArrayTypeParser -from .boolean_type_parser import BooleanTypeParser as BooleanTypeParser -from .float_type_parser import FloatTypeParser as FloatTypeParser -from .allof_type_parser import AllOfTypeParser as AllOfTypeParser diff --git a/jambo/parser/_type_parser.py b/jambo/parser/_type_parser.py index 16dd833..e64ae77 100644 --- a/jambo/parser/_type_parser.py +++ b/jambo/parser/_type_parser.py @@ -1,8 +1,9 @@ -from abc import ABC, abstractmethod -from typing import Generic, TypeVar +from pydantic import Field from typing_extensions import Self -from pydantic import Field +from abc import ABC, abstractmethod +from typing import Generic, TypeVar + T = TypeVar("T") diff --git a/jambo/parser/array_type_parser.py b/jambo/parser/array_type_parser.py index 04bde0b..20a6125 100644 --- a/jambo/parser/array_type_parser.py +++ b/jambo/parser/array_type_parser.py @@ -1,13 +1,12 @@ -import copy - from jambo.parser._type_parser import GenericTypeParser - -from typing import TypeVar - from jambo.utils.properties_builder.mappings_properties_builder import ( mappings_properties_builder, ) +import copy +from typing import TypeVar + + V = TypeVar("V") diff --git a/jambo/types/json_schema_type.py b/jambo/types/json_schema_type.py index 77ec7ff..0658db6 100644 --- a/jambo/types/json_schema_type.py +++ b/jambo/types/json_schema_type.py @@ -1,4 +1,4 @@ -from typing import List, Dict, Union, TypedDict, Literal +from typing import Dict, List, Literal, TypedDict, Union JSONSchemaType = Literal[ diff --git a/pyproject.toml b/pyproject.toml index 9660943..2b83921 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ build-backend = "hatchling.build" # Linters -[tool.ruff] +[tool.ruff.lint] extend-select = ["I"] [tool.ruff.lint.isort] diff --git a/tests/parser/test_allof_type_parser.py b/tests/parser/test_allof_type_parser.py new file mode 100644 index 0000000..9edc865 --- /dev/null +++ b/tests/parser/test_allof_type_parser.py @@ -0,0 +1,288 @@ +from jambo.parser.allof_type_parser import AllOfTypeParser + +from unittest import TestCase + + +class TestAllOfTypeParser(TestCase): + def test_all_of_type_parser_object_type(self): + """ + Test the AllOfTypeParser with an object type and validate the properties. + When using allOf with object it should be able to validate the properties + and join them correctly. + """ + properties = { + "type": "object", + "allOf": [ + { + "properties": { + "name": { + "type": "string", + "minLength": 1, + } + }, + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 4, + }, + "age": { + "type": "integer", + "maximum": 100, + "minimum": 0, + }, + }, + }, + ], + } + + type_parsing, type_validator = AllOfTypeParser.from_properties( + "placeholder", properties + ) + + with self.assertRaises(ValueError): + type_parsing(name="John", age=101) + + with self.assertRaises(ValueError): + type_parsing(name="", age=30) + + with self.assertRaises(ValueError): + type_parsing(name="John Invalid", age=30) + + obj = type_parsing(name="John", age=30) + self.assertEqual(obj.name, "John") + self.assertEqual(obj.age, 30) + + def test_all_of_type_parser_object_type_required(self): + """ + Tests the required properties of the AllOfTypeParser with an object type. + """ + + properties = { + "type": "object", + "allOf": [ + { + "properties": { + "name": { + "type": "string", + } + }, + "required": ["name"], + }, + { + "type": "object", + "properties": { + "age": { + "type": "integer", + } + }, + "required": ["age"], + }, + ], + } + + type_parsing, type_validator = AllOfTypeParser.from_properties( + "placeholder", properties + ) + + with self.assertRaises(ValueError): + type_parsing(name="John") + + with self.assertRaises(ValueError): + type_parsing(age=30) + + obj = type_parsing(name="John", age=30) + self.assertEqual(obj.name, "John") + self.assertEqual(obj.age, 30) + + def test_all_of_type_top_level_type(self): + """ + Tests the AllOfTypeParser with a top-level type and validate the properties. + """ + + properties = { + "type": "string", + "allOf": [ + {"maxLength": 11}, + {"maxLength": 4}, + {"minLength": 1}, + {"minLength": 2}, + ], + } + + type_parsing, type_validator = AllOfTypeParser.from_properties( + "placeholder", properties + ) + + self.assertEqual(type_parsing, str) + self.assertEqual(type_validator["max_length"], 11) + self.assertEqual(type_validator["min_length"], 1) + + def test_all_of_type_parser_in_fields(self): + """ + Tests the AllOfTypeParser when set in the fields of a model. + """ + properties = { + "allOf": [ + {"type": "string", "maxLength": 11}, + {"type": "string", "maxLength": 4}, + {"type": "string", "minLength": 1}, + {"type": "string", "minLength": 2}, + ] + } + + type_parsing, type_validator = AllOfTypeParser.from_properties( + "placeholder", properties + ) + + self.assertEqual(type_parsing, str) + self.assertEqual(type_validator["max_length"], 11) + self.assertEqual(type_validator["min_length"], 1) + + def test_invalid_all_of(self): + """ + Tests that an error is raised when the allOf type is not present. + """ + properties = { + "wrongKey": [ + {"type": "string", "maxLength": 11}, + {"type": "string", "maxLength": 4}, + {"type": "string", "minLength": 1}, + {"type": "string", "minLength": 2}, + ] + } + + with self.assertRaises(ValueError): + AllOfTypeParser.from_properties("placeholder", properties) + + def test_all_of_invalid_type_not_present(self): + properties = { + "allOf": [ + {"maxLength": 11}, + {"maxLength": 4}, + {"minLength": 1}, + {"minLength": 2}, + ] + } + + with self.assertRaises(ValueError): + AllOfTypeParser.from_properties("placeholder", properties) + + def test_all_of_invalid_type_in_fields(self): + properties = { + "allOf": [ + {"type": "string", "maxLength": 11}, + {"type": "integer", "maxLength": 4}, + {"type": "string", "minLength": 1}, + {"minLength": 2}, + ] + } + + with self.assertRaises(ValueError): + AllOfTypeParser.from_properties("placeholder", properties) + + def test_all_of_description_field(self): + """ + Tests the AllOfTypeParser with a description field. + """ + + properties = { + "type": "object", + "allOf": [ + { + "properties": { + "name": { + "type": "string", + "description": "One", + } + }, + }, + { + "properties": { + "name": { + "type": "string", + "description": "Of", + } + }, + }, + { + "properties": { + "name": { + "type": "string", + "description": "Us", + } + }, + }, + ], + } + + type_parsing, _ = AllOfTypeParser.from_properties("placeholder", properties) + + self.assertEqual( + type_parsing.schema()["properties"]["name"]["description"], + "One | Of | Us", + ) + + def test_all_of_with_defaults(self): + """ + Tests the AllOfTypeParser with a default value. + """ + + properties = { + "type": "object", + "allOf": [ + { + "properties": { + "name": { + "type": "string", + "default": "John", + } + }, + }, + { + "properties": { + "age": { + "type": "integer", + "default": 30, + } + }, + }, + ], + } + + type_parsing, _ = AllOfTypeParser.from_properties("placeholder", properties) + obj = type_parsing() + self.assertEqual(obj.name, "John") + self.assertEqual(obj.age, 30) + + def test_all_of_with_conflicting_defaults(self): + """ + Tests the AllOfTypeParser with conflicting default values. + """ + + properties = { + "type": "object", + "allOf": [ + { + "properties": { + "name": { + "type": "string", + "default": "John", + } + }, + }, + { + "properties": { + "name": { + "type": "string", + "default": "Doe", + } + }, + }, + ], + } + + with self.assertRaises(ValueError): + AllOfTypeParser.from_properties("placeholder", properties) diff --git a/tests/parser/test_array_type_parser.py b/tests/parser/test_array_type_parser.py index 9c06a46..4177cce 100644 --- a/tests/parser/test_array_type_parser.py +++ b/tests/parser/test_array_type_parser.py @@ -1,8 +1,8 @@ +from jambo.parser import ArrayTypeParser + from typing import get_args from unittest import TestCase -from jambo.parser import ArrayTypeParser - class TestArrayTypeParser(TestCase): def test_array_parser_no_options(self): diff --git a/tests/parser/test_bool_type_parser.py b/tests/parser/test_bool_type_parser.py index 761ddc0..1ba25aa 100644 --- a/tests/parser/test_bool_type_parser.py +++ b/tests/parser/test_bool_type_parser.py @@ -1,7 +1,7 @@ -from unittest import TestCase - from jambo.parser import BooleanTypeParser +from unittest import TestCase + class TestBoolTypeParser(TestCase): def test_bool_parser_no_options(self): diff --git a/tests/parser/test_float_type_parser.py b/tests/parser/test_float_type_parser.py index c8e3ad5..c25ab49 100644 --- a/tests/parser/test_float_type_parser.py +++ b/tests/parser/test_float_type_parser.py @@ -1,7 +1,7 @@ -from unittest import TestCase - from jambo.parser import FloatTypeParser +from unittest import TestCase + class TestFloatTypeParser(TestCase): def test_float_parser_no_options(self): diff --git a/tests/parser/test_int_type_parser.py b/tests/parser/test_int_type_parser.py index e50b340..64da2bd 100644 --- a/tests/parser/test_int_type_parser.py +++ b/tests/parser/test_int_type_parser.py @@ -1,7 +1,7 @@ -from unittest import TestCase - from jambo.parser import IntTypeParser +from unittest import TestCase + class TestIntTypeParser(TestCase): def test_int_parser_no_options(self): diff --git a/tests/parser/test_object_type_parser.py b/tests/parser/test_object_type_parser.py index 1c1fea7..6f56727 100644 --- a/tests/parser/test_object_type_parser.py +++ b/tests/parser/test_object_type_parser.py @@ -1,7 +1,7 @@ -from unittest import TestCase - from jambo.parser import ObjectTypeParser +from unittest import TestCase + class TestObjectTypeParser(TestCase): def test_object_type_parser(self): diff --git a/tests/parser/test_string_type_parser.py b/tests/parser/test_string_type_parser.py index f5d19fe..9cdf901 100644 --- a/tests/parser/test_string_type_parser.py +++ b/tests/parser/test_string_type_parser.py @@ -1,7 +1,7 @@ -from unittest import TestCase - from jambo.parser import StringTypeParser +from unittest import TestCase + class TestStringTypeParser(TestCase): def test_string_parser_no_options(self):