Simple configurable conversion of dataclasses to raw data
Project description
Dataclass As Data
This is a simple package for configurable conversion of dataclasses to a data representation, typically a dict or a tuple. The behaviour for how a dataclass is converted to and from data can be configured to differ from the default per dataclass if desired.
This package only supports simple primitive types, other dataclasses, and the primitive generics dict[...]
, list[...]
, tuple[...]
, Union[...]
, and Optional[...]
as type annotations by default.
Single-Input functions can be used in the place of type hints as simple converters.
Install
This package supports Python 3.9 and above.
pip install dataclass-as-data
Quick Start
import dataclasses
from dataclass_as_data import as_data, from_data
# Create a dataclass
@dataclasses.dataclass
class Person:
name: str
age: int
# Create a dataclass object
person = Person("Simon", 21)
>>> person
Person(name='Simon', age=21)
# Call as_data with the dataclass object to convert it to a dictionary
data = as_data(person)
>>> data
{'name': 'Simon', 'age': 21}
# Call from_data with the dataclass and the data to get the object instance back
>>> from_data(Person, data)
Person(name='Simon', age=21)
Dataclasses can be nested within dataclasses, which are recursively converted to their data representation.
@dataclasses.dataclass
class Friends:
people: list[Person]
# All dataclasses are converted recursively
>>> as_data(Friends([Person("Sunset", 22), Person("Starlight", 20)]))
{'people': [{'name': 'Sunset', 'age': 22}, {'name': 'Starlight', 'age': 20}]}
>>> from_data(Friends, {'people': [{'name': 'Sunset', 'age': 22}, {'name': 'Starlight', 'age': 20}]})
Friends(people=[Person(name='Sunset', age=22), Person(name='Starlight', age=20)])
Configuring as_data and from_data
To change what data is constructed when using as_data
and from_data
, override the as_data
method and from_data
class methods in your dataclass.
Note: you must use one of as_dict
, as_tuple
, from_dict
, or from_tuple
(not as_data
or from_data
) if you wish to use the default behaviour and modify it.
from dataclass_as_data import as_data, as_dict, from_data, from_dict
@dataclasses.dataclass
class Config:
VERSION = (1, 0)
version: tuple[int, int] = VERSION
def as_data(self) -> dict:
# Ensure correct version when converting to data
assert self.version == self.VERSION, "Incorrect version!"
return as_dict(self) # use as_dict to otherwise use default behaviour
@classmethod
def from_data(cls, data: dict):
# Update version on data load
if data['version'] < cls.VERSION:
data['version'] = cls.VERSION
return from_dict(cls, data) # use from_dict to otherwise use default behaviour
# Now these methods are called instead
>>> as_data(Config((0, 1)))
AssertionError: Incorrect version!
>> from_data(Config, {'version': (0, 1)})
Config(version=(1, 0))
DataAsTuple
If you'd simply like a dataclass to be represented as a tuple instead of a dict when calling as_data
,
inherent from the DataAsTuple
abstract base class.
from dataclass_as_data import as_data, DataAsTuple
# Create a dataclass inheriting from DataAsTuple
@dataclasses.dataclass
class Person(DataAsTuple):
name: str
age: int
# Calling as_data now returns a tuple
>>> as_data(Person("Summer", 24))
("Summer", 24)
This merely overrides as_data
and from_data
to use as_tuple
and from_tuple
for you respectively.
from dataclass_as_data import as_tuple, from_tuple
# Same as inheriting from DataAsTuple
@dataclasses.dataclass
class Person:
name: str
age: int
def as_data(self):
return as_tuple(self)
@classmethod
def from_data(cls, data: tuple):
return from_tuple(cls, data)
Custom converters
from_data
supports very basic custom converters in the form on single-input functions.
These converters are called on the relevant data entries when from_data
is called.
Note that regular types, such as int
, are also technically treated this way.
Note: as_data
performs no type conversion at all.
from dataclass_as_data import from_data
def lower_str(value) -> str:
"""Convert to lowercase str"""
return str(value).lower()
@dataclasses.dataclass
class Employee:
id: int
name: lower_str
# The `lower_str` converter is called on the value of the `name` parameter
>>> from_data(Employee, {'id': 123, 'name': "Sylvester"})
Employee(id=123, name='sylvester')
# The string value of `id` is coerced into an int
>>> from_data(Employee, {'id': "456", 'name': "Sunny"})
Employee(id=456, name='sunny')
Class conversion
If you have your own classes that you'd like to use in type hints,
you can also configure regular classes to be converted to and from data in a certain way by defining an
as_data
method and from_data
class method.
Note: using your own classes in type hints is not supported by default.
from dataclass_as_data import as_data, from_data
class Name:
def __init__(self, full_name):
self.first, self.last = full_name.split(" ")
def as_data(self):
return f"{self.last}, {self.first}"
@classmethod
def from_data(cls, data):
last, first = data.split(", ")
return cls(f"{first} {last}")
def __repr__(self):
return f"{type(self).__name__}('{self.first} {self.last}')"
@dataclasses.dataclass
class Student:
name: Name
# Data for the class is now represented and converted as desired
>>> as_data(Student(Name("Silver Spoon")))
{'name': 'Spoon, Silver'}
>>> from_data(Student, {'name': 'Spoon, Silver'})
Student(name=Name('Silver Spoon'))
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for dataclass_as_data-0.1.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 93020809ab42fb770b9cf78040250b0d84353e2d4fa82c5cd9ac02d3f84074c6 |
|
MD5 | 73bbeaa40e4d847e4290869ecbec4155 |
|
BLAKE2b-256 | 80dd1d887bf970ff2024be08f425aef69de8add2940d9db5bfe0fb68a8d29614 |