Merge pull request #28 from HideyoshiNakazone/feature/adds-enums
[Feature] Adds Enums
This commit was merged in pull request #28.
This commit is contained in:
37
docs/source/usage.enum.rst
Normal file
37
docs/source/usage.enum.rst
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
Enum Type
|
||||||
|
==================
|
||||||
|
|
||||||
|
An enum type is a special data type that enables a variable to be a set of predefined constants. The enum type is used to define variables that can only take one out of a small set of possible values.
|
||||||
|
|
||||||
|
It does not have any specific properties, but it has the generic properties:
|
||||||
|
|
||||||
|
- default: Default value for the enum.
|
||||||
|
- description: Description of the enum field.
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from jambo import SchemaConverter
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
"title": "EnumExample",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["active", "inactive", "pending"],
|
||||||
|
"description": "The status of the object.",
|
||||||
|
"default": "active",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": ["status"],
|
||||||
|
}
|
||||||
|
|
||||||
|
Model = SchemaConverter.build(schema)
|
||||||
|
|
||||||
|
obj = Model(status="active")
|
||||||
|
print(obj) # Output: EnumExample(status=status.ACTIVE)
|
||||||
@@ -44,4 +44,5 @@ For more complex schemas and types see our documentation on
|
|||||||
usage.object
|
usage.object
|
||||||
usage.reference
|
usage.reference
|
||||||
usage.allof
|
usage.allof
|
||||||
usage.anyof
|
usage.anyof
|
||||||
|
usage.enum
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
# Exports generic type parser
|
|
||||||
from ._type_parser import GenericTypeParser
|
from ._type_parser import GenericTypeParser
|
||||||
|
|
||||||
# Exports Implementations
|
|
||||||
from .allof_type_parser import AllOfTypeParser
|
from .allof_type_parser import AllOfTypeParser
|
||||||
from .anyof_type_parser import AnyOfTypeParser
|
from .anyof_type_parser import AnyOfTypeParser
|
||||||
from .array_type_parser import ArrayTypeParser
|
from .array_type_parser import ArrayTypeParser
|
||||||
from .boolean_type_parser import BooleanTypeParser
|
from .boolean_type_parser import BooleanTypeParser
|
||||||
|
from .enum_type_parser import EnumTypeParser
|
||||||
from .float_type_parser import FloatTypeParser
|
from .float_type_parser import FloatTypeParser
|
||||||
from .int_type_parser import IntTypeParser
|
from .int_type_parser import IntTypeParser
|
||||||
from .object_type_parser import ObjectTypeParser
|
from .object_type_parser import ObjectTypeParser
|
||||||
@@ -15,6 +13,7 @@ from .string_type_parser import StringTypeParser
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"GenericTypeParser",
|
"GenericTypeParser",
|
||||||
|
"EnumTypeParser",
|
||||||
"AllOfTypeParser",
|
"AllOfTypeParser",
|
||||||
"AnyOfTypeParser",
|
"AnyOfTypeParser",
|
||||||
"ArrayTypeParser",
|
"ArrayTypeParser",
|
||||||
|
|||||||
32
jambo/parser/enum_type_parser.py
Normal file
32
jambo/parser/enum_type_parser.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from jambo.parser._type_parser import GenericTypeParser
|
||||||
|
from jambo.types.type_parser_options import TypeParserOptions
|
||||||
|
|
||||||
|
from typing_extensions import Unpack
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class EnumTypeParser(GenericTypeParser):
|
||||||
|
json_schema_type = "enum"
|
||||||
|
|
||||||
|
def from_properties_impl(
|
||||||
|
self, name, properties, **kwargs: Unpack[TypeParserOptions]
|
||||||
|
):
|
||||||
|
if "enum" not in properties:
|
||||||
|
raise ValueError(f"Enum type {name} must have 'enum' property defined.")
|
||||||
|
|
||||||
|
enum_values = properties["enum"]
|
||||||
|
|
||||||
|
if not isinstance(enum_values, list):
|
||||||
|
raise ValueError(f"Enum type {name} must have 'enum' as a list of values.")
|
||||||
|
|
||||||
|
# Create a new Enum type dynamically
|
||||||
|
enum_type = Enum(name, {str(value).upper(): value for value in enum_values})
|
||||||
|
parsed_properties = self.mappings_properties_builder(properties, **kwargs)
|
||||||
|
|
||||||
|
if (
|
||||||
|
"default" in parsed_properties and parsed_properties["default"] is not None
|
||||||
|
):
|
||||||
|
parsed_properties["default"] = enum_type(parsed_properties["default"])
|
||||||
|
|
||||||
|
return enum_type, parsed_properties
|
||||||
@@ -50,6 +50,7 @@ repository = "https://github.com/HideyoshiNakazone/jambo.git"
|
|||||||
create-hooks = "bash .githooks/set-hooks.sh"
|
create-hooks = "bash .githooks/set-hooks.sh"
|
||||||
tests = "python -m coverage run -m unittest discover -v"
|
tests = "python -m coverage run -m unittest discover -v"
|
||||||
tests-report = "python -m coverage xml"
|
tests-report = "python -m coverage xml"
|
||||||
|
serve-docs = "sphinx-autobuild docs/source docs/build"
|
||||||
|
|
||||||
# Build System
|
# Build System
|
||||||
[tool.hatch.version]
|
[tool.hatch.version]
|
||||||
|
|||||||
80
tests/parser/test_enum_type_parser.py
Normal file
80
tests/parser/test_enum_type_parser.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
from jambo.parser import EnumTypeParser
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnumTypeParser(TestCase):
|
||||||
|
def test_enum_type_parser_throws_enum_not_defined(self):
|
||||||
|
parser = EnumTypeParser()
|
||||||
|
|
||||||
|
schema = {}
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parsed_type, parsed_properties = parser.from_properties_impl(
|
||||||
|
"TestEnum",
|
||||||
|
schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_enum_type_parser_throws_enum_not_list(self):
|
||||||
|
parser = EnumTypeParser()
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
"enum": "not_a_list",
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parsed_type, parsed_properties = parser.from_properties_impl(
|
||||||
|
"TestEnum",
|
||||||
|
schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_enum_type_parser_creates_enum(self):
|
||||||
|
parser = EnumTypeParser()
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
"enum": ["value1", "value2", "value3"],
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed_type, parsed_properties = parser.from_properties_impl(
|
||||||
|
"TestEnum",
|
||||||
|
schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(parsed_type, type)
|
||||||
|
self.assertTrue(issubclass(parsed_type, Enum))
|
||||||
|
self.assertEqual(
|
||||||
|
set(parsed_type.__members__.keys()), {"VALUE1", "VALUE2", "VALUE3"}
|
||||||
|
)
|
||||||
|
self.assertEqual(parsed_properties, {"default": None})
|
||||||
|
|
||||||
|
def test_enum_type_parser_creates_enum_with_default(self):
|
||||||
|
parser = EnumTypeParser()
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
"enum": ["value1", "value2", "value3"],
|
||||||
|
"default": "value2",
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed_type, parsed_properties = parser.from_properties_impl(
|
||||||
|
"TestEnum",
|
||||||
|
schema,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsInstance(parsed_type, type)
|
||||||
|
self.assertTrue(issubclass(parsed_type, Enum))
|
||||||
|
self.assertEqual(
|
||||||
|
set(parsed_type.__members__.keys()), {"VALUE1", "VALUE2", "VALUE3"}
|
||||||
|
)
|
||||||
|
self.assertEqual(parsed_properties["default"].value, "value2")
|
||||||
|
|
||||||
|
def test_enum_type_parser_throws_invalid_default(self):
|
||||||
|
parser = EnumTypeParser()
|
||||||
|
|
||||||
|
schema = {
|
||||||
|
"enum": ["value1", "value2", "value3"],
|
||||||
|
"default": "invalid_value",
|
||||||
|
}
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
parser.from_properties_impl("TestEnum", schema)
|
||||||
@@ -597,3 +597,40 @@ class TestSchemaConverter(TestCase):
|
|||||||
self.assertEqual(obj.age, 30)
|
self.assertEqual(obj.age, 30)
|
||||||
self.assertEqual(obj.address.street, "123 Main St")
|
self.assertEqual(obj.address.street, "123 Main St")
|
||||||
self.assertEqual(obj.address.city, "Springfield")
|
self.assertEqual(obj.address.city, "Springfield")
|
||||||
|
|
||||||
|
def test_enum_type_parser(self):
|
||||||
|
schema = {
|
||||||
|
"title": "Person",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["active", "inactive", "pending"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["status"],
|
||||||
|
}
|
||||||
|
|
||||||
|
Model = SchemaConverter.build(schema)
|
||||||
|
|
||||||
|
obj = Model(status="active")
|
||||||
|
self.assertEqual(obj.status.value, "active")
|
||||||
|
|
||||||
|
def test_enum_type_parser_with_default(self):
|
||||||
|
schema = {
|
||||||
|
"title": "Person",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["active", "inactive", "pending"],
|
||||||
|
"default": "active",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["status"],
|
||||||
|
}
|
||||||
|
|
||||||
|
Model = SchemaConverter.build(schema)
|
||||||
|
|
||||||
|
obj = Model()
|
||||||
|
self.assertEqual(obj.status.value, "active")
|
||||||
|
|||||||
Reference in New Issue
Block a user