Skip to content

Quickstart

Installation

CleanChausie requires Python 3.8+. To install, run python3 -m pip install cleanchausie.

Declaring a Schema

Declarative schemas inherit from cleanchausie.Schema. In its simplest form:

import datetime
from typing import Optional
from cleanchausie import Schema, clean, serialize

class JobApplication(Schema):
    name: str
    middle_initial: Optional[str] = None
    referrer: Optional[str]
    timestamp: datetime.datetime

external_data = {
    'name': 'John Doe',
    'referrer': None,
    'timestamp': '2020-01-01T00:00:00Z',
}
validation_result = clean(JobApplication, external_data)
print(serialize(validation_result))
"""
{
    'name': 'John Doe',
    'middle_initial': None,
    'referrer': None,
    'timestamp': '2020-01-01T00:00:00+00:00'
}
"""

What's going on here? Let's break it down:

  • Inheriting from Schema gives us a few things:
    • A clean classmethod, which takes in a dictionary and returns a Schema instance if validation passes, or a ValidationError if validation fails.
    • A serialize method, which returns a dictionary representation of the schema instance.
  • name is annotated as str. CleanChausie automatically turns this into a StringField, which will only accept string values.
  • middle_initial is an not required, and if omitted is set to None.
  • referrer is required, but is also nullable. Omitting it will result in a validation error, but an explicit None is completely valid.
  • timestamp is our first non-string field. It's a datetime and is required. Datetime-ey strings are turned into datetime objects.

If validation fails, instead of returning an instance of JobApplication, we get a ValidationError instance instead.

external_data = {
    'name': 5,
    'timestamp': 'The first monday after Jan 1 1970',
}
validation_result = clean(JobApplication, external_data)
print(serialize(validation_result))
"""
{
    'errors': [
        {
            'field': ('timestamp',),
            'msg': "Could not parse datetime from 'The first monday after Jan 1 1970'"
        },
        {'field': ('referrer',), 'msg': 'This field is required.'},
        {'field': ('name',), 'msg': "Expected a string, got 'int'."}
    ]
}
"""

But why CleanChausie?

Types

CleanChausie operates on/with type-checked objects that have good IDE/autocomplete support. Simple fields can be declared with just annotations.

Errors are returned instead of raised, which means mypy will help you catch validation errors.

Composable

Fields and field validation logic are composable and reusable. The declarative Schema class is a wrapper around a more functional core, which leans into composition over inheritance. It's easy to write and reuse custom fields which compose and extend existing fields.

Context

Validation supports (but does not require) passing around an explicit context to any field validation function that requests it. It's only used within validation logic, is not stored with validated data. Contexts are commonly used to avoid global state, or for explicit session management on database lookups.

Nullable vs Omitted

Presence validation and value validation are treated as distinct first-class citizens. Fields can be required, required and nullable, omittable (with an omitted singleton default), or omittable with a different default value.

Cleanchausie understands Union[OMITTED, str] annotations, and can automatically parse all the above scenarios.

Intra-schema dependencies

Fields within a schema can depend on the validated value of other fields in the same schema. Validated values are passed to dependent fields as args (using dependency injection), and the field validation order is handled automatically.