-from typing import List, Dict, Tuple
+from typing import List, Dict, Tuple, Union
from strictyaml import Map, Str, EmptyDict, Int, Float, Seq, MapPattern, FixedSeq
+import strictyaml
+
+
+class _DummyType:
+ pass
+
+
+NoneType = type(None)
+
_TYPE_MAP = {
int: Int,
List: Seq,
Dict: MapPattern,
Tuple: FixedSeq,
+ Union: _DummyType,
}
-_FIELD_NAME = "STRICTYAML_SCHEMA"
+_SCHEMA_FIELD_NAME = "STRICTYAML_SCHEMA"
class StrictYAMLSchemaGenerationError(Exception):
def _get_strictyaml_type(python_type):
- if hasattr(python_type, _FIELD_NAME):
- return getattr(python_type, _FIELD_NAME)
+ # another already processed class
+ if hasattr(python_type, _SCHEMA_FIELD_NAME):
+ return getattr(python_type, _SCHEMA_FIELD_NAME)
+ # compount types like List
elif (
hasattr(python_type, "__origin__")
and hasattr(python_type, "__args__")
origin = getattr(python_type, "__origin__")
args = getattr(python_type, "__args__")
+ # special case for Optional[T]
+ if origin == Union and len(args) == 2 and args[1] == NoneType:
+ return strictyaml.Optional(_get_strictyaml_type(args[0]))
+
type_constructor = _TYPE_MAP[origin]
type_arguments = [_get_strictyaml_type(a) for a in args]
print(type_constructor, type_arguments)
+
+ # special case for Tuple
if origin == Tuple:
return type_constructor(type_arguments)
- else:
- return type_constructor(*type_arguments)
+ # default behaviour
+ return type_constructor(*type_arguments)
+
+ # error handlers for non existent primitive types
elif python_type not in _TYPE_MAP:
raise StrictYAMLSchemaGenerationError(
f"Type {python_type} is not supported for YAML schema generation"
)
+ # remaining primitive and untyped types
else:
return _TYPE_MAP[python_type]()
-def dataclasses_strictyaml_schema(cls):
+def dataclass_strictyaml_schema(cls):
anot = cls.__dict__.get("__annotations__", {})
if len(anot) == 0:
fields[name] = _get_strictyaml_type(python_type)
schema = Map(fields)
- setattr(cls, _FIELD_NAME, schema)
+ setattr(cls, _SCHEMA_FIELD_NAME, schema)
return cls
-from knot_resolver_manager.utils import dataclasses_strictyaml_schema
-from typing import List, Dict, Tuple
+from knot_resolver_manager.utils import dataclass_strictyaml_schema
+from typing import List, Dict, Optional, Tuple
from strictyaml import Map, Str, EmptyDict, Int, Float, Seq, MapPattern, FixedSeq
+import strictyaml
import pytest
def test_empty_class():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
pass
def test_int_field():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: int
def test_string_field():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: str
def test_float_field():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: float
def test_multiple_fields():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field1: str
field2: int
def test_list_field():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: List[str]
def test_dict_field():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: Dict[str, int]
)
+def test_optional_field():
+ @dataclass_strictyaml_schema
+ class TestClass:
+ field: Optional[int]
+
+ assert _schema_eq(
+ TestClass.STRICTYAML_SCHEMA, Map({"field": strictyaml.Optional(Int())})
+ )
+
+
def test_nested_dict_list():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: Dict[str, List[int]]
List can't be a dict key, so this should fail
"""
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: Dict[List[int], List[int]]
def test_nested_list():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: List[List[List[List[int]]]]
def test_tuple_field():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: Tuple[str, int]
def test_nested_tuple():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: Tuple[str, Dict[str, int], List[List[int]]]
def test_chained_classes():
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: int
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class CompoundClass:
c: TestClass
from dataclasses import dataclass
@dataclass
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
class TestClass:
field: int
def test_combined_with_dataclass2():
from dataclasses import dataclass
- @dataclasses_strictyaml_schema
+ @dataclass_strictyaml_schema
@dataclass
class TestClass:
field: int