Svarog allow to create object from non typed data. All it need is annotated __init__ method:
>>> from svarog import forge
... class A:
... def __init__(self, a: int, b: str):
... self._a = a
... self._b = b
... def __repr__(self):
... return f'A(a={self._a}, b="{self._b}")'
>>> forge(A, {"a": 1, "b": "3"})
A(a=1, b="3")
More complicated types as Sequence, Mapping, Optional are possible
>>> class A:
... def __init__(self, b: Sequence[int]):
... self._b = b
... def __repr__(self):
... return f'A(b={self._b})'
>>> forge(A, {"b": "3213"})
A(b=[3, 2, 1, 3])
You can use forward refs:
>>> class WithRef:
... def __init__(self, child: Optional['WithRef']):
... self._child = child
... def __repr__(self):
... return f"WithRef({self._child!r})"
>>> forge(WithRef(WithRef(WithRef())))
WithRef(WithRef(WithRef(None)))
Objects are forged recursively:
>>> @dataclass
... class A:
... b: 'B'
... c: 'C'
... @dataclass
... class B:
... number: int
... @dataclass
... class C:
... string: str
>>> forge(A, {'b': {'number': 42}, 'c': {'string': 'the-string'}})
A(b=B(number=42), c=C(string='the-string'))
You can register own forge for your classes:
>>> class FooType(Enum):
... LOREM = "lorem"
... IPSUM = "ipsum"
...
... class FooParams:
... types: ClassVar[Mapping[FooType, "FooParams"]] = {}
... def __init_subclass__(cls, type: FooType):
... cls.types[type] = cls
...
... @classmethod
... def for_type(cls, type):
... return cls.types[type]
...
... @dataclass
... class LoremFooParams(FooParams, type=FooType.LOREM):
... lorem: str
...
... @dataclass
... class IpsumFooParams(FooParams, type=FooType.IPSUM):
... ipsum: int
...
... @dataclass
... class Foo:
... type: FooType
... params: FooParams
...
... @classmethod
... def forge(cls, _, data, forge):
... foo_type = forge(FooType, data["type"])
... return Foo(
... type=forge(FooType, foo_type),
... params=forge(FooParams.for_type(foo_type), data["params"])
... )
...
>>> register_forge(Foo, Foo.forge)
>>> forge(Foo, {"type": "lorem", "params": {"lorem": "foo-bar"}})
Foo(type=<FooType.LOREM: 'lorem'>, params=LoremFooParams(lorem='foo-bar'))
>>> forge(Foo, {"type": "ipsum", "params": {"ipsum": 42}})
Foo(type=<FooType.IPSUM: 'ipsum'>, params=IpsumFooParams(ipsum=42))
Support for CamelCase to snake_case convertion:
>>> class Snake:
... lorem_ipsum: int
>>> forge = Svarog(snake_case=True).forge
>>> forge(Snake, {"LoremIpsum": 42})
Snake(lorem_ipsum=42)
- Free software: MIT license
- Documentation: https://svarog.readthedocs.io.
- Converts unstructured data into structured recursively
- Works with dataclasses
- Works with Sequence, Mapping, Optional
- Special conventers for types can be registered with
Some parts of this code, and concept borrowed from cattrs project
This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.