Convert dictionaries to dataclasses and back
Project description
howard
Python datatype marshalling
This library marshalls dictionaries [read json] into instances of defined dataclasses and back.
i.e.
from dataclasses import dataclass
import howard
@dataclass
class Person:
name: str
age: int
my_dict = {'name': 'Bob', 'age': 24}
person = howard.from_dict(my_dict, Person)
assert person.name == 'Bob'
assert person.age == 24
The main purpose is to use python dataclasses as a schema definition rather than having to hand-write a schema. Howard does not currently include logic itself for generating json-schema (swagger) object documentation, but that is a long term goal of it.
Howard differs from a standard dataclass because it can recursively marshall and unmarshall.
It also supports more types out of the box than dataclasses.asdict
does. Some supported types:
- Enums
- TypedDict
- Collections (lists/dictionaries)
- Datetime
- all primitives (int/string/boolean/float)
All of the logic for howard is in your dataclass definition, not in howard. Howard just has a to_dict
and from_dict
method,
and it bases all decisions off of your dataclass. There is no inheritance on custom types, everything is standard, built-in python. (3.7+)
Installing
pip install howard
More examples
For more examples, you can go look at the tests at tests/test_howard.py
Here is a basic example of recursive types and how it can work with howard:
from enum import Enum
from dataclasses import dataclass, field
from typing import List
import howard
@dataclass
class Suit(Enum):
heart = 'h'
spade = 's'
diamond = 'd'
club = 'c'
def validate_rank(i: int) -> int:
lb, ub = 1, 13
if lb <= i <= ub:
return i
raise ValueError(f'{i} is not between {lb} and {ub}')
@dataclass
class Card:
rank: int = field(metadata={'howard': {'decoder': validate_rank}})
suit: Suit
@dataclass
class Hand:
hand_id: int = 0
cards: List[Card] = field(default_factory=list)
d = {'hand_id': 2, 'cards': [{'rank': 2, 'suit': 'c'}, {'rank': 10, 'suit': 'h'}]}
# d is a dictionary, now we turn it into the dataclass
obj = howard.from_dict(d, Hand)
assert isinstance(obj, Hand)
assert obj.hand_id == 2
assert len(obj.cards) == 2
assert isinstance(obj.cards[0], Card)
assert obj.cards[0].suit == Suit.club
# and back to a dictionary
json_dict = howard.to_dict(obj)
In the above example, you can see a couple things.
- A
Hand
contains a list ofCard
. - The sub-object
Card
also gets unmarshalled correctly. - The
Suit
object is an enum, which is like a string in json form, but only has 4 possible values. - The
Card
has a field calledrank
which has its own custom decoder. In this case, the decoder acts as a validator, but can also be used for custom decode logic.
FAQ
-
Why not just use
dataclasses.asdict
andMyDataclass(**my_dict)
?dataclasses.asdict
doesn't work on all types, for example, Enums or datetimes.MyDataclass(**my_dict)
will not recursively turn the subobjects into their respective datatype. -
What about custom types? You can specify custom decoders and encoders in a dataclass
field.metadata
section. See example above.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file howard-1.12.1.tar.gz
.
File metadata
- Download URL: howard-1.12.1.tar.gz
- Upload date:
- Size: 4.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.20.1 setuptools/49.1.0 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.8.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b69582b260cd370c6d6799b54ccc8a5a0b8ef85fe98aa6534d3bac54673e0d50 |
|
MD5 | 1730e7d0bb51a242658e8057d8495a7d |
|
BLAKE2b-256 | 3a979a903689e0742ac44ece26287915aa7379cce455fc27e91bffd7f1802547 |