Koda Validate#
Koda Validate is a library and toolkit for building composable and typesafe validators. In many cases, validators can be derived from typehints automatically (e.g. TypedDicts, dataclasses, and NamedTuples). For everything else, you can compose validator callables or write your own. At its heart, Koda Validate is just a few kinds of functions that fit together, so the possibilities are endless. It is async-friendly and comparable in performance to Pydantic 2.
Koda Validate can be used in normal control flow or as a runtime type checker.
Basic Usage#
from koda_validate import StringValidator
my_first_validator = StringValidator()
Easy enough. Let’s see how it works:
>>> my_first_validator("a string")
Valid(val='a string')
>>> my_first_validator(0)
Invalid(err_type=TypeErr(expected_type=<class 'str'>), ...)
For both valid and invalid cases, a value is returned – no exceptions are raised.
Working with Valid
and Invalid
types is covered more in
Validation Results.
Collections#
from koda_validate import ListValidator, IntValidator
list_int_validator = ListValidator(IntValidator())
Nesting validators works as one might expect.
>>> list_int_validator([1,2,3])
Valid(val=[1, 2, 3])
Derived Validators#
Koda Validate can inspect typehints and build Validator
s automatically.
from typing import List, TypedDict
from koda_validate import TypedDictValidator
class Person(TypedDict):
name: str
hobbies: List[str]
validator = TypedDictValidator(Person)
Usage:
>>> validator({"name": "Bob", "hobbies": ["eating", "coding", "sleeping"]})
Valid(val={'name': 'Bob', 'hobbies': ['eating', 'coding', 'sleeping']})
See Derived Validators for more.
Refinement#
from koda_validate import StringValidator, MinLength, MaxLength, StartsWith
validator = StringValidator(MinLength(5),
MaxLength(10),
StartsWith("a"))
print(validator("abc123"))
Outputs:
Valid(val='abc123')
Note
MinLength(5)
, MaxLength(10)
, and StartsWith("a")
are all Predicates.
Nested Validators#
We can build complex nested Validator
s with ease.
from typing import Annotated, Union, TypedDict, Literal, List
from koda_validate import TypedDictValidator
class Group(TypedDict):
name: str
members: List[str]
class Song(TypedDict):
artist: Union[List[str], Group, Literal["unknown"]]
title: str
duration_seconds: int
song_validator = TypedDictValidator(Song)
stonehenge = {
"artist": {
"name": "Spinal Tap",
"members": ["David St. Hubbins", "Nigel Tufnel", "Derek Smalls"]
},
"title": "Stonehenge",
"duration_seconds": 276
}
drinkinstein = {
"artist": ["Sylvester Stallone", "Dolly Parton"],
"title": "Drinkin' Stein",
"duration_seconds": 215
}
Usage:
>>> song_validator(stonehenge)
Valid(...)
>>> song_validator(drinkinstein)
Valid(...)
>>> song_validator({
... "artist": "unknown",
... "title": "druids chanting, archival recording number 10",
... "duration_seconds": 4_000
... })
Valid(...)
It’s easy to keep nesting validators:
from koda_validate import ListValidator, MinItems
songs_validator = ListValidator(song_validator, predicates=[MinItems(2)])
class Playlist(TypedDict):
title: str
songs: Annotated[List[Song], songs_validator]
playlist_validator = TypedDictValidator(Playlist)
Note
TypedDictValidator
, DataclassValidator
and NamedTupleValidator
will use Annotated Validators if found.
Usage:
>>> playlist_validator({
... "title": "My Favorite Songs",
... "songs": [stonehenge, drinkinstein]
... })
Valid(...)
Custom Validators#
from typing import Any
from koda_validate import Validator, ValidationResult, Valid, Invalid, TypeErr
class IntegerValidator(Validator[int]):
def __call__(self, val: Any) -> ValidationResult[int]:
if isinstance(val, int):
return Valid(val)
else:
return Invalid(TypeErr(int), val, self)
Usage:
>>> validator = IntegerValidator()
>>> validator(5)
Valid(val=5)
>>> invalid_result = validator("not an integer")
>>> invalid_result.err_type
TypeErr(expected_type=<class 'int'>)
In Koda Validate, you are encouraged to write your own Validator
s for custom
needs. As long as you obey the typing rules when building custom Validator
s,
you should be able to combine them with built-in Validator
s, however you wish.
For guidance, take a look at Extension.
Contents#
- koda_validate
AlwaysValid
BoolValidator
BytesValidator
CacheValidatorBase
Choices
Coercer
CoercionErr
ContainerErr
DataclassValidator
DateValidator
DatetimeValidator
DecimalValidator
DictValidatorAny
EmailPredicate
EndsWith
EqualTo
EqualsValidator
ExactItemCount
ExactLength
ExtraKeysErr
FloatValidator
IndexErrs
IntValidator
Invalid
IsDictValidator
KeyErrs
KeyNotRequired
KeyValErrs
Lazy
ListValidator
LowerCase
MapErr
MapValidator
Max
MaxItems
MaxKeys
MaxLength
Min
MinItems
MinKeys
MinLength
MissingKeyErr
MultipleOf
NTupleValidator
NamedTupleValidator
NoneValidator
NotBlank
OptionalValidator
Predicate
PredicateAsync
PredicateErrs
Processor
RecordValidator
RegexPredicate
SetErrs
SetValidator
StartsWith
StringValidator
TypeErr
TypedDictValidator
UUIDValidator
UniformTupleValidator
UnionErrs
UnionValidator
UniqueItems
UpperCase
Valid
ValidationErrBase
Validator
coercer()
ValidationResult
ErrType
- koda_validate.serialization
- koda_validate.signature