- Published on
pydictable
Origin
As part of my work at MyShubhLife, we needed a solution to serialize and deserialize the python objects on multiple instances. Tricky part is, even the serialized content had to be readable either by human or other sytems. What else could be it other than JSON. When we deserialize it back, we wanted it to be a python object with native type hints. Following is the example
json = """{"name": "Pramod"}"""
obj = convert(json)
obj.name # Should be accessable, with pycharm or any IDE type hints
Eventually, we understood that we can use the above convert(...)
function to validate the json data. We had different implementations of this, in-place, in multiple services. It quickly came back biting us and we had to solve it across the services we had.
These problems led me to start building a library to solve exactly two issues
- Schema validation
- Serialization with type hints
I cannot deny talking about Pydantic. We actually tried adopting it. There were few problems which stopped us from integrating it
- It was pretty heavy in size. We were hitting the max size of the packaged service
- We were not so happy in using a library which has multiple dependencies
- It was too early to use a beast like Pydantic. Needed a simple solution
I started this with just a file couple of years back but multiple teams quickly started adopting it. I decided to make it a independent pip package. Currently, the code is hosted at https://github.com/pskd73/pydictable as completely open source project.
Usage
Install it like any other python package
pip install pydictable
Start using it by defining the schemas. You can simply use native python type hints and it just works! You can nest schemas also.
class LatLng(DictAble):
lat: int
lng: int
class Address(DictAble):
pin_code: int
lat_lng: LatLng
class Person(DictAble):
name: str
address: Address
Just pass the python dict
to the DictAble
s.
input_dict = {
'name': 'Pramod',
'address': {
'pin_code': 560032,
'lat_lng': {
'lat': 12345,
'lng': 67890
}
}
}
p = Person(dict=input_dict)
It will throw DataValidationError
error if any issue with the input dict
passed.
Extendability
By design, this package is built for extendability. You can created your own fields and validators.
class PositiveIntField(IntField):
def validate_json(self, field_name: str, v):
assert v > 0, 'Should be positive integer'
class Person(DictAble):
age: int = PositiveIntField()
Person(dict={'age': -1}) # Raises DataValidationError
Polymorphism
It can even support polymorphism out of the box.
class Homo(DictAble):
name: str
class Neanderthal(Homo):
animals_killed: int
class Sapien(Homo):
words_spoken: int
class Human(DictAble):
species: Homo = MultiTypeField([Neanderthal, Sapien])
human = Human(dict={
'species': {
'name': 'Mufasa',
'words_spoken': 1024,
'__type': 'Sapien'
}
})
assert human.species.name == 'Mufasa'
assert isinstance(human.species, Sapien)
assert human.species.words_spoken == 1024
As a team, we are regularly maintaining it. Our vision is to keep it as simple as possible, pure python, and zero dependency solution. Feel free to try it out, report issues, and send pull requests. Cheers :)