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'>),
value=0,
validator=StringValidator()
)
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 Validators automatically.
from typing import 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 Validators with ease.
from typing import Annotated, Union, TypedDict, Literal
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 Validators for custom
needs. As long as you obey the typing rules when building custom Validators,
you should be able to combine them with built-in Validators, however you wish.
For guidance, take a look at Extension.
Contents¶
Getting Started
Examples and Guides
API Reference
- koda_validate
AlwaysValidBoolValidatorBytesValidatorCacheValidatorBaseChoicesCoercerCoercionErrContainerErrDataclassValidatorDateValidatorDatetimeValidatorDecimalValidatorDictValidatorAnyEmailPredicateEndsWithEqualToEqualsValidatorExactItemCountExactLengthExtraKeysErrFloatValidatorIndexErrsIntValidatorInvalidIsDictValidatorKeyErrsKeyNotRequiredKeyValErrsLazyListValidatorLowerCaseMapErrMapValidatorMaxMaxItemsMaxKeysMaxLengthMinMinItemsMinKeysMinLengthMissingKeyErrMultipleOfNTupleValidatorNamedTupleValidatorNoneValidatorNotBlankOptionalValidatorPredicatePredicateAsyncPredicateErrsProcessorRecordValidatorRegexPredicateSetErrsSetValidatorStartsWithStringValidatorTypeErrTypedDictValidatorUUIDValidatorUniformTupleValidatorUnionErrsUnionValidatorUniqueItemsUpperCaseValidValidationErrBaseValidatorcoercer()ValidationResultErrType
- koda_validate.serialization
- koda_validate.signature
Philosophy