#!/usr/bin/env python3
"""Utilities for dealing with Dataclasses. A non-official type hint and some
friendly wrappers around conversion to/from Dicts."""
from __future__ import annotations
import dataclasses
from typing import Any, Dict, Protocol
[docs]
class Dataclass(Protocol):
"""Dataclass isn't really a first class type and therefore there is no offical
type hint for Dataclasses in Python (yet). If you need one, here's a suitable
stand in. Example usage::
def f(d: Dataclass) -> Any:
pass
def g(d: Dict[str, Any]) -> Dataclass:
pass
"""
__dataclass_fields__: Dict
[docs]
def dataclass_from_dict(dataclass: type, d: Dict[str, Any]) -> Dataclass:
"""Given a Dataclass type and a dict, return a populated instance.
Args:
dataclass: the Class type to return an instance of
d: the dict to be used to populate the new instance
Returns:
A constructed and populated dataclass instance.
>>> from dataclasses import dataclass
>>> from datetime import date
>>> @dataclass
... class Record:
... name: str
... phone: str
... address: str
... age: int
... member_since: date
...
>>> d = {
... 'name': 'John Smith',
... 'phone': '555-1234',
... 'address': '994 Main St.',
... 'age': 26,
... 'member_since': date(2006, 5, 14),
... }
>>> dataclass_from_dict(Record, d)
Record(name='John Smith', phone='555-1234', address='994 Main St.', age=26, member_since=datetime.date(2006, 5, 14))
"""
fields = {f.name for f in dataclasses.fields(dataclass) if f.init}
filtered_args = {k: v for k, v in d.items() if k in fields}
return dataclass(**filtered_args)
[docs]
def dataclass_to_dict(dataclass: Dataclass) -> Dict[str, Any]:
"""
Returns:
A dict-representation of a valid dataclass.
>>> from dataclasses import dataclass
>>> from datetime import date
>>> @dataclass
... class Record:
... name: str
... phone: str
... address: str
... age: int
... member_since: date
...
>>> r = Record(name='Jane Doe', phone='555-1232', address='998 Main St.', age=23, member_since=date(2008, 3, 1))
>>> dataclass_to_dict(r)
{'name': 'Jane Doe', 'phone': '555-1232', 'address': '998 Main St.', 'age': 23, 'member_since': datetime.date(2008, 3, 1)}
"""
assert dataclasses.is_dataclass(dataclass)
return dataclasses.asdict(dataclass)
if __name__ == '__main__':
import doctest
doctest.testmod()