Encodium¶
Encodium is a simple serialization and validation library.
Getting started¶
Here’s an example object to get you started:
from encodium import Encodium, Integer, String, Boolean
class Person(Encodium):
age = Integer.Definition(non_negative=True)
name = String.Definition(max_length=50)
diabetic = Boolean.Definition(default=True)
And here’s what it looks in use:
# raises ValidationError("Age cannot be negative").
impossible = Person(age=-1, name='Impossible')
# raises ValidationError("Name must not be None").
nameless = Person(age=25)
# Works.
john = Person(age=25, name='John', diabetic=False)
# Does json.
json_representation = john.to_json()
new_john = Person.from_json(json_representation)
# Can read in an object from a socket.
foreign_person = Person.recv(sock)
# Can send an object over a socket.
john.send(sock)
Validation¶
Most validation in Encodium is performed automatically by the Definition objects that are set as class variables. For example:
from encodium import Encodium, Integer, String, Boolean
class Person(Encodium):
age = Integer.Definition(non_negative=True)
hat = String.Definition(default="Fedora")
Each attribute is checked against it’s definition when the Person is created:
john = Person(age=-1)
The following arguments are included by default:
- optional – Whether or not the attribute is allowed to be None.
- default – The default value to set the attribute to, if it is not provided.
Some examples:
# Raises ValidationError("Age cannot be None")
john = Person()
# lucy.hat will be set to "Fedora"
lucy = Person(age=25)
Type checking is also included automatically:
john = Person(age="this is not an integer")
Constraints can be implemented by defining check_attribute() on the type’s Definition class, as thus:
from encodium import Encodium, ValidationError
class Integer(int):
class Definition(Encodium.Definition):
def check_attribute(self, value):
if self.non_negative and value < 0:
raise ValidationError('cannot be negative')
More complex validation can be done by defining check() on the object.
A useful paradigm when using encodium is to use the following invariant: If the object exists, then it is valid.
Here’s an example to illustrates this:
from encodium import Encodium, Bytes, ValidationError
import hashlib
class DataSHA256(Encodium):
data = Bytes.Definition()
sha256sum = Bytes.Definition()
def check(self, changed_attributes):
if 'data' in changed_attributes:
expected_hash = hashlib.sha256(self.data).digest()
else:
# The data hasn't changed, so the current hash is valid.
expected_hash = self.sha256sum
if self.hash != expected_hash:
raise ValidationError('has an invalid hash')
Recursive Definitions¶
Sometimes it’s necessary to have recursive definitions. However, python doesn’t allow a class to reference itself during construction.
To overcome this, Encodium.Definition('ClassName', ...) may be used instead of ClassName.Definition(...), as thus:
from encodium import Encodium, String
class Tree(Encodium):
left = Encodium.Definition('Tree', optional=True)
right = Encodium.Definition('Tree', optional=True)
value = String.Definition()