Research¶
This presents an overview of existing packages that might serve as inspiration or possibly could be plugged in.
Validation¶
An (incomplete) overview of existing validation packages (in alphabetical order).
Cerberus¶
Cerberus is a lightweight and extensible data validation library for Python.
>>> schema = {'name': {'type': 'string'}}
>>> v = Validator(schema)
>>> document = {'name': 'john doe'}
>>> v.validate(document)
True
Chainable Validators¶
>>> import re
>>> from validator import *
>>> spec = {
... 'foo': [required, istype(int)],
... 'bar': [optional, match(re.compile(r'te.*')],
... 'baz': [optional, boolean],
... }
Colander¶
A serialization/deserialization/validation library for strings, mappings and lists.
import colander
class Friend(colander.TupleSchema):
rank = colander.SchemaNode(
colander.Int(),
validator=colander.Range(0, 9999)
)
name = colander.SchemaNode(colander.String())
class Phone(colander.MappingSchema):
location = colander.SchemaNode(
colander.String(),
validator=colander.OneOf(['home', 'work'])
)
number = colander.SchemaNode(colander.String())
class Friends(colander.SequenceSchema):
friend = Friend()
class Phones(colander.SequenceSchema):
phone = Phone()
class Person(colander.MappingSchema):
name = colander.SchemaNode(colander.String())
age = colander.SchemaNode(
colander.Int(),
validator=colander.Range(0, 200)
)
friends = Friends()
phones = Phones()
django-nap <https://django-nap.readthedocs.io/>¶
Schema generates from model:
class QuestionMapper(mapper.ModelMapper):
class Meta:
model = models.Question
fields = '__all__'
Data binding:
>>> q = Question.objects.first()
>>> m = QuestionMapper(q)
>>> m.question_text
"What's new?"
>>> m.pub_date
'2017-06-17 05:30:58+00:00'
>>> m.question_text = "So, what is new?"
>>> q.question_text
'So, what is new?'
>>> m.pub_date = '1975-11-05 23:30:00'
>>> q.pub_date
datetime.datetime(1975, 11, 5, 23, 30)
formencode.validator¶
A validation library for Python.
>>> class Registration(formencode.Schema):
... first_name = validators.ByteString(not_empty=True)
... last_name = validators.ByteString(not_empty=True)
... email = validators.Email(resolve_domain=True)
... username = formencode.All(
... validators.PlainText(),
... UniqueUsername()
... )
... password = SecurePassword()
... password_confirm = validators.ByteString()
... chained_validators = [
... validators.FieldsMatch('password', 'password_confirm'),
... ]
Gladiator¶
Gladiator is a Data Validation Framework for Python3
import gladiator as gl
registration_form_validator = (
('email', gl.required, gl.format_email),
('pw', gl.required, gl.length_min(5)),
('name', gl.required, gl.type_(str)),
('birth_year', gl.required, gl.type_(int), gl.value_max(2014 - 18)),
)
valid_test_data = {
'email': 'test@example.com',
'pw': 'password123',
'name': 'Test Username',
'birth_year': 1984,
}
result = gl.validate(registration_form_validator, valid_test_data)
assert result.success
good¶
Slim yet handsome validation library.
from good import Schema, Entire
def maxkeys(n):
# Return a validator function
def validator(d):
# `d` is the dictionary.
# Validate it
assert len(d) <= 3, 'Dict size should be <= 3'
# Return the value since all callable schemas should do that
return d
return validator
schema = Schema({
str: int,
Entire: maxkeys(3),
})
incoming¶
JSON validation framework for Python.
>>> class AddressValidator(PayloadValidator):
... street = datatypes.String()
... country = datatypes.String()
...
>>> class PersonValidator(PayloadValidator):
... name = datatypes.String()
... age = datatypes.Integer()
... address = datatypes.JSON(AddressValidator)
...
>>> PersonValidator().validate({
... 'name': 'Some name',
... 'age': 19,
... 'address': {'street': 'Brannan, SF', 'country': 'USA'},
... })
(True, None)
>>> PersonValidator().validate({
... 'name': 'Some name',
... 'age': 19,
... 'address': {'street': 'Brannan, SF', 'country': 0},
... })
(False, {'address': ['Invalid data. Expected JSON.', {'country': ['Invalid
data. Expected a string.']}]})
Kanone¶
A general purpose validation library
>>> from kanone import *
>>> HelloSchema = Schema(
... 'nick', String() & Len(max=20),
... 'email', web.Email(),
... 'email_confirm', Match(Field('.email'), ignoreCase=True)
... )
>>> context = HelloSchema.context({
... 'nick': 'bob',
... 'email': 'Bob@Some.Domain.Org',
... 'email_confirm': 'BOB@Some.domain.org',
... })
>>> context('nick').result
u'bob'
>>> context('email').result
u'Bob@some.domain.org'
lasso¶
Lightweight module to define serializable, schema-validated classes
>>> class Name(lasso.Schemed):
... __schema__ = {"first": str, "family": str}
...
>>> class User(lasso.Schemed):
... __schema__ = {"name": Name, "email": str}
...
>>> jdoe = User(
... name=Name(first="John", family="Doe"),
... email="j@doe.org"
... )
marshmallow: simplified object serialization¶
marshmallow is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.
from marshmallow import Schema, fields
class ArtistSchema(Schema):
name = fields.Str()
class AlbumSchema(Schema):
title = fields.Str()
artist = fields.Nested(ArtistSchema)
schema = AlbumSchema()
result = schema.dump(album)
print(result.data)
- django-rest-marshmallow: Marshmallow schemas for Django REST framework
- marshmallow-form: a wrapper of marshmallow for form library like behavior
- marshmallow-validators: Use 3rd-party validators (e.g. from WTForms and colander) with marshmallow
- webargs: A friendly library for parsing HTTP request arguments
Naval¶
Python validation library with error messages in multiple languages and a readable syntax.
>>> from naval import *
>>> # we're going to use the passlib library to encrypt passwords
>>> from passlib.hash import bcrypt
>>> registration_form = Schema(
['username', Type(str), Length(min=3, max=16)],
['password', Type(str)],
['password2'],
[
Assert(
(lambda d: d['password'] == d['password2']),
error_message = "Passwords don't match"
)
],
['password', lambda s: s.encode('utf-8'), bcrypt.encrypt, Save],
['password2', Delete],
['email', Email]
)
>>> registration_form.validate({
'email': 'the-king@example.com',
'username': 'TheKing',
'password': 'hackme',
'password2': 'hackme',
})
{'email': 'the-king@example.com',
'password': '$2a$12$JT2UlXP0REt3EX7kGIFGV.5uKPQJL4phDRpfcplW91sJAyB8RuKwm',
'username': 'TheKing'}
notario¶
Validation of Python dictionaries
>>> data = {'main': {'foo': 'bar'}}
>>> schema = ('main', MultiRecursive(('foo', 1), ('foo', 'bar')))
>>> validate(data, schema)
schema¶
Schema validation just got Pythonic
>>> from schema import Schema, And, Use, Optional
>>> schema = Schema([{
... 'name': And(str, len),
... 'age': And(Use(int), lambda n: 18 <= n <= 99),
... Optional('sex'): And(
... str, Use(str.lower), lambda s: s in ('male', 'female')
... )
... }])
>>> data = [
... {'name': 'Sue', 'age': '28', 'sex': 'FEMALE'},
... {'name': 'Sam', 'age': '42'},
... {'name': 'Sacha', 'age': '20', 'sex': 'Male'},
... ]
>>> validated = schema.validate(data)
>>> assert validated == [
... {'name': 'Sue', 'age': 28, 'sex': 'female'},
... {'name': 'Sam', 'age': 42},
... {'name': 'Sacha', 'age' : 20, 'sex': 'male'},
... ]
Schematics¶
Python Data Structures for Humans™.
>>> from schematics.models import Model
>>> from schematics.types import StringType
>>> class Person(Model):
... name = StringType()
... bio = StringType(required=True)
...
>>> p = Person()
>>> p.name = 'Paul Eipper'
>>> p.validate()
Traceback (most recent call last):
...
ModelValidationError: {'bio': [u'This field is required.']}
sigma.core¶
sigma.core is a validation framework.
from sigma.core import Model, ErrorContainer, asdict, validate
from sigma.standard import Field
class User(Model):
id = Field(type=int, size=(5, 10))
password = Field(type=str, length=(8, 15))
user = User()
user.id = 5
user.password = "12345678"
asdict(user) # {"id": 5, "password": "12345678"}
val¶
A validator for arbitrary Python objects.
>>> from val import Schema
>>> sub_schema = Schema({'foo': str, str: int})
>>> schema = Schema({
... 'key1': sub_schema,
... 'key2': sub_schema,
... str: sub_schema,
... })
>>> schema.validates({
... 'key1': {'foo': 'bar'},
... 'key2': {'foo': 'qux', 'baz': 43},
... 'whatever': {'foo': 'doo', 'fsck': 22, 'tsk': 2992},
... })
True
valhalla¶
Minimalist validation library with focus on API brevity and simplicity. 40+ filters primitive and composed.
my_definition = {
# email address with alternate name
'email': ['require', ('alt', 'email_address'), 'email'],
# age must be numeric between 13 and 100
'age': ['require', 'numeric', ('range', 13, 100)],
'password': [('text', 10, 50)],
'password_confirm': [('match', 'password')]
}
s = Schema.from_dict(my_definition)
s.validate(some_data) # Bam!
valideer¶
Lightweight data validation and adaptation Python library.
>>> import valideer as V
>>> product_schema = {
... "+id": "number",
... "+name": "string",
... "+price": V.Range("number", min_value=0),
... "tags": ["string"],
... "stock": {
... "warehouse": "number",
... "retail": "number",
... },
... }
>>> validator = V.parse(product_schema)
Validation¶
Validation is a small python library to validate python data structures.
import validation
# Build the validation model
user_validator = validation.Dict()
user_validator.required['_id'] = validation.StringUUID()
user_validator.required['name'] = validation.String()
user_validator.required['gender'] = validation.Choice(
choices=['male', 'female']
)
hobbies = validation.List()
hobbies.validator = validation.String()
user_validator.optional['hobbies'] = validation.List()
# two valid user objects
john = {
'_id': 'e7a5ff1c-ee5e-4ca9-a3d3-0106dd826dcd',
'name': 'John',
'gender': 'male',
'hobbies:': [
'python',
'blarg',
'blub'
],
}
paula = {
'_id': 'e7a5ff1c-ee5e-4ca9-a3d3-0106dd826dcd',
'name': 'Paula',
'gender': 'female',
}
# an not valid one
weirdo = {
'_id': 'e7a5ff1c-ee5e-4ca9-a3d3-0106dd826dcd',
'name': 'Weirdo',
'gender': 'all of them',
'hobbies:': [
'mitosis',
],
}
for user in [john, paula, weirdo]:
try:
# None is returned of the user is valid
user_validator.validate(john)
except validation.ValidationError as err:
# a exception is raised, if the object is invalid
# the exception message contains the first failed element
print(err)
Validator¶
A library for validating that dictionary values meet certain sets of parameters. Much like form validators, but for dicts.
from validator import Required, Not, Truthy, Blank, Range, Equals, In, validate
# let's say that my dictionary needs to meet the following rules...
rules = {
"foo": [Required, Equals(123)],
"bar": [Required, Truthy()],
"baz": [In(["spam", "eggs", "bacon"])],
"qux": [Not(Range(1, 100))], # by default, Range is inclusive
}
# then this following dict would pass:
passes = {
"foo": 123,
"bar": True, # or a non-empty string, or a non-zero int, etc...
"baz": "spam",
"qux": 101,
}
print validate(rules, passes)
# (True, {})
# but this one would fail
fails = {
"foo": 321,
"bar": False, # or 0, or [], or an empty string, etc...
"baz": "barf",
"qux": 99,
}
print validate(rules, fails)
# (False,
# {
# 'foo': ["must be equal to '123'"],
# 'bar': ['must be True-equivalent value'],
# 'baz': ["must be one of ['spam', 'eggs', 'bacon']"],
# 'qux': ['must not fall between 1 and 100']
# })
Validators¶
Python Data Validation for Humans™.
>>> import validators
>>> validators.email('someone@example.com')
True
voluptuous¶
Voluptuous, despite the name, is a Python data validation library. It is primarily intended for validating data coming into Python as JSON, YAML, etc.
>>> from voluptuous import Required, All, Length, Range
>>> schema = Schema({
... Required('q'): All(str, Length(min=1)),
... Required('per_page', default=5): All(int, Range(min=1, max=20)),
... 'page': All(int, Range(min=0)),
... })
WTForms¶
A flexible forms validation and rendering library for Python.
from wtforms import Form, BooleanField, StringField, validators
class RegistrationForm(Form):
username = StringField(
'Username', [validators.Length(min=4, max=25)]
)
email = StringField(
'Email Address', [validators.Length(min=6, max=35)]
)
accept_rules = BooleanField(
'I accept the site rules', [validators.InputRequired()]
)
def register(request):
form = RegistrationForm(request.POST)
if request.method == 'POST' and form.validate():
user = User()
user.username = form.username.data
user.email = form.email.data
user.save()
redirect('register')
return render_response('register.html', form=form)