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.