RecordValidator#

In many cases RecordValidator is more verbose than the Derived Validators, but it comes with greater flexibility. It can handle any kind of Hashable key. Optional keys are handled explicitly with KeyNotRequired, which returns Maybe values. The into parameter can be any Callable with the correct type signature – dataclasses are convenient, but it could just as well be an arbitrary function.

from dataclasses import dataclass
from koda import Maybe, Just
from koda_validate import (RecordValidator, StringValidator, not_blank, MaxLength,
                           Min, Max, IntValidator, KeyNotRequired, Invalid, Valid)


@dataclass
class Person:
    name: str
    age: Maybe[int]


person_validator = RecordValidator(
    into=Person,
    keys=(
        ("full name", StringValidator(not_blank, MaxLength(50))),
        ("age", KeyNotRequired(IntValidator(Min(0), Max(130)))),
    ),
)


match person_validator({"full name": "John Doe", "age": 30}):
    case Valid(person):
        match person.age:
            case Just(age):
                age_message = f"{age} years old"
            case nothing:
                age_message = "ageless"
        print(f"{person.name} is {age_message}")
    case Invalid(errs):
        print(errs)

Output:

John Doe is 30 years old

Here’s a more complex example of mixing and matching different kinds of keys.

from typing import List
from dataclasses import dataclass
from koda import Maybe, Just
from koda_validate import (
    RecordValidator, StringValidator, KeyNotRequired, IntValidator, Valid, ListValidator
)


@dataclass
class Person:
    name: str
    age: Maybe[int]
    hobbies: List[str]


person_validator = RecordValidator(
    into=Person,
    keys=(
        (1, StringValidator()),
        (False, KeyNotRequired(IntValidator())),
        (("abc", 123), ListValidator(StringValidator()))
    ),
)

assert person_validator({
    1: "John Doe",
    False: 30,
    ("abc", 123): ["reading", "cooking"]
}) == Valid(Person(
    "John Doe",
    Just(30),
    ["reading", "cooking"]
))

Caveats#

The main caveats with RecordValidator are:

  • it works on a maximum of 16 keys

  • type checkers don’t always produce the most readable hints and errors for RecordValidator, as it uses @overloads.

  • the target of validation must be defined outside the RecordValidator, and the order of arguments matters