https://github.com/julian-nash/liaison
A Python library for defining schemas, parsing and validating payloads
https://github.com/julian-nash/liaison
parsing payloads python validation
Last synced: 3 days ago
JSON representation
A Python library for defining schemas, parsing and validating payloads
- Host: GitHub
- URL: https://github.com/julian-nash/liaison
- Owner: Julian-Nash
- License: mit
- Created: 2021-10-08T14:52:58.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2021-10-10T14:39:23.000Z (over 3 years ago)
- Last Synced: 2025-06-03T15:27:41.718Z (13 days ago)
- Topics: parsing, payloads, python, validation
- Language: Python
- Homepage:
- Size: 39.1 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Liaison
A zero dependency Python library for defining schemas, parsing and validating payloads.
[](https://github.com/Julian-Nash/liaison/actions/workflows/main.yml)
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 coverageInstallation:
```shell
pip install liaison
```Example:
```py3
from liaison import Schema, ValidationError
from liaison.fields import StringField, IntField, BoolField, ListField, DateTimeFieldclass 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": "[email protected]",
"age": 21,
"tags": ["Python"]
}result = UserSchema.parse(data)
print(result.name, result.email, result.age, result.tags) # Bar [email protected] 21 ['Python']
```Handling validation errors:
```py3
data = {
"name": "Bar",
"email": "[email protected]",
"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 `.validator` decorator:
```py3
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:
```py3
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 valueclass 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.```py3
from liaison import Schema
from liaison.fields import StringField, IntField, BoolField, FloatField, UUIDFieldclass 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:
```py3
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:
```py3
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)
```py3
from liaison import Schema, ValidationError
from liaison.fields import StringFieldclass 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 valueclass 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!
```