Skip to main content

Liaison is a Python library for defining schemas, parsing and validating payloads

Project description

Liaison

A zero dependency Python library for defining schemas, parsing and validating payloads.

CI

Liaison doesn't aim to be too clever. It doesn't use descriptors, fancy metaprogramming or type hints for defining your schema. Simply inherit from the Schema base class, define your fields and call parse. In return, you'll receive a simple Namespace object containing your parsed data.

Goals:

  • Simplicity
  • Extensibility
  • Speed
  • 100% test coverage

Installation:

pip install liaison

Example:

from liaison import Schema, ValidationError
from liaison.fields import StringField, IntField, BoolField, ListField, DateTimeField


class UserSchema(Schema):

    name = StringField(required=True)
    email = StringField(required=True)
    age = IntField(min_val=18)
    date_of_birth = DateTimeField(date_format="%d-%m-%Y")
    subscribed = BoolField(default=False)
    tags = ListField(min_len=1)


data = {
    "name": "Bar",
    "email": "foo@bar.com",
    "age": 21,
    "tags": ["Python"]
}

result = UserSchema.parse(data)

print(result.name, result.email, result.age, result.tags)  # Bar foo@bar.com 21 ['Python']

Handling validation errors:

data = {
    "name": "Bar",
    "email": "foo@bar.com",
    "age": 16
}

try:
    result = UserSchema.parse(data)
except ValidationError as e:
    print(e)  # Value for 'age' must be at least 18

Defining custom field validators via the <field>.validator decorator:

class UserSchema(Schema):

    name = StringField(required=True)
    email = StringField(required=True)
    age = IntField(min_val=18)

    @name.validator
    def validate_name(self, key, value):
        # Define a custom validator, overrides the default validation method
        if value == "Foo":
            raise ValidationError(f"'{value}' is not a valid value for '{key}'")
        return value

Custom validators can also be passed as a parameter to the field:

def name_validator(schema_cls, key, value):
    if value in ("Foo", "Bar", "Baz"):
        raise ValidationError(f"'{value}' is not a valid value for '{key}'")
    return value


class UserSchema(Schema):

    name = StringField(required=True, validator=name_validator)
    email = StringField(required=True)
    age = IntField(min_val=18)

Fields

Use fields to define your schema. By default, all fields accept the following common parameters:

Parameter Type Description Default
required bool If the value is required False
default Any A default value None
choices List[Any] A list of choices None
validator Callable A function to override the default validation method None
strict_type bool If True, only accept the fields data type False

StringField - Defining strings

Parameter Type Description Default
min_len int The minimum length None
max_len int The maximum length None

IntField - Defining integers

Parameter Type Description Default
min_val int The minimum value None
max_val int The maximum value None

FloatField - Defining floats

Parameter Type Description Default
min_val int The minimum value None
max_val int The maximum value None

BoolField - Defining booleans

ListField - Defining lists

Parameter Type Description Default
min_len int The minimum length None
max_len int The maximum length None

SetField - Defining sets

Note - SetField shares the same behaviour as ListField, returning a set.

Parameter Type Description Default
min_len int The minimum length None
max_len int The maximum length None

DictField - Defining dictionaries

Parameter Type Description Default
min_len int The minimum length None
max_len int The maximum length None

DateTimeField - Defining datetimes

Note - DateTimeField fields will return datetime objects

Parameter Type Description
date_format str The date format

UUIDField - Defining UUIDs

Note - UUIDField fields will NOT return a UUID obejct, it will return a string.

Namespace

Calling the parse method on a Schema object will return a Namespace object, holding the parsed values as attributes.

from liaison import Schema
from liaison.fields import StringField, IntField, BoolField, FloatField, UUIDField


class RESTBaseSchema(Schema):

    offset = IntField(min_val=0, default=0)
    limit = IntField(max_val=100)
    search = StringField()


class ProductsRESTSchema(RESTBaseSchema):

    product_id = UUIDField()
    category = StringField()
    price = FloatField()
    in_stock = BoolField()


payload = {
    "offset": 10,
    "category": "shoes",
    "in_stock": True
}

result = ProductsRESTSchema.parse(payload)  

print(result.offset, result.limit, result.search, result.category, result.in_stock)
# 10 None None shoes True

Namespace objects have a to_dict method, returning a dictionary of the Namespace attributes and values:

print(result.to_dict())
# {'category': 'shoes', 'in_stock': True, 'limit': None, 'offset': 10, 'price': None, 'product_id': None, 'search': None}

An optional exclude parameter can be included to exclude certain attributes:

print(result.to_dict(exclude=("offset", "limit", "search")))
# {'category': 'shoes', 'in_stock': True, 'price': None, 'product_id': None}

Defining custom fields

Create your own fields and validation logic by inheriting from any of the field classes and implementing a validate method.

Note - The validate method must accept 2 params (key, value)

from liaison import Schema, ValidationError
from liaison.fields import StringField


class PasswordField(StringField):

    def validate(self, key, value):
        value = super().validate(key, value)
        if len(value) < 9:
            raise ValidationError("Value for 'password' must be at least 9 characters in length")
        # etc...
        return value


class UserSchema(Schema):

    username = StringField(required=True)
    password = PasswordField()


payload = {
    "username": "FooBar",
    "password": "password"
}

try:
    result = UserSchema.parse(payload)
except ValidationError as e:
    print(e)  # Value for 'password' must be at least 9 characters in length
    
payload = {
    "username": "FooBar",
    "password": "password12345!"
}

result = UserSchema.parse(payload)
print(result.password)  # password12345!

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

liaison-0.3.tar.gz (5.7 kB view hashes)

Uploaded Source

Built Distribution

liaison-0.3-py3-none-any.whl (6.1 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page