Skip to content

Null vs Omission

Nullability is explicit, and CleanChausie differentiates between:

  • the field is required
    • value is non-nullable
    • value is nullable (if None is explicitly passed)
  • the field is omittable
    • the value is non-nullable
    • the value is nullable
    • the field has a default value (or value factory)

These variants can either be expressed explicitly, or CleanChausie will define them automatically to match a Schema's type annotations.

from typing import Optional, Union
from cleanchausie.consts import OMITTED
from cleanchausie.fields import field, StrField, Omittable, Required
from cleanchausie.schema import Schema

# auto define fields based on annotations
class NullabilityExample(Schema):
    # field is required
    nonnull_required: str
    nullable_required: Optional[str]

    # field is omittable
    nonnull_omittable: Union[str, OMITTED]
    nonnull_omittable_with_default: str = "default"

    nullable_omittable: Optional[Union[str, OMITTED]]
    nullable_omittable_with_default: Optional[str] = None

# or define the same fields explicitly
class NullabilityExplicitExample(Schema):
    # field is required
    nonnull_required = field(StrField())
    nullable_required = field(StrField(), nullability=Required(allow_none=True))

    # field is omittable
    nonnull_omittable = field(StrField(), nullability=Omittable(allow_none=False))
    nonnull_omittable_with_default = field(
        StrField(),
        nullability=Omittable(allow_none=False, omitted_value="default")
    )

    nullable_omittable = field(StrField(), nullability=Omittable(allow_none=True))
    nullable_omittable_with_default = field(
        StrField(), nullability=Omittable(allow_none=True, omitted_value=None)
    )

For lists and other mutable types, an explicit field definition can specify an omitted_value_factory instead:

from cleanchausie import field, ListField, Omittable, Schema, StrField

class ListExample(Schema):
    omittable_list_with_default = field(
        ListField(StrField()),
        nullability=Omittable(allow_none=True, omitted_value_factory=list)
    )

OMITTED vs omitted?

CleanChausie uses a special omitted singleton to represent the absence of a value. Singletons are not well-supported by static type checkers, so omitted is implemented as a single-value enum (which is valid within a Literal).

OMITTED is just an alias for Literal[omitted], and is used in type annotations.

The drawback of this approach is that it's not possible to control implicit behavior like __bool__ or __repr__.

Fallback Utility

Want to use omitted within your schema, but not within downstream code? CleanChausie provides a fallback utility to help:

from cleanchausie import OMITTED, Schema, clean, fallback
from .service import downstream_func, empty

class MySchema(Schema):
    my_field: str | OMITTED

def process(data: dict):
    result = clean(MySchema, data)
    assert isinstance(result, MySchema)

    return downstream_func(
      val=fallback(result.my_field, empty)
    )