Validation Results¶
In Koda Validate, Validators express validation success or failure by returning
a ValidationResult. To be more specific it requires one generic parameter: the
valid data type. Likewise, Validators take the same
generic parameter for the same purpose. So, a Validator[int] will always return a
ValidationResult[int]:
validator: Validator[int] = IntValidator()
result = validator(5)
assert result.is_valid
assert isinstance(result.val, int) # mypy also knows ``result.val`` is an ``int``
Note
ValidationResult[int] is a more concise way to express Union[Valid[int], Invalid],
to which it is exactly equivalent.
Branching on Validity¶
As you can see, to do something useful with ValidationResults, we need to
distinguish between the Valid and Invalid variants, as each
has different attributes.
if Statements¶
Perhaps the easiest way is to just branch on .is_valid:
from koda_validate import ValidationResult, StringValidator
def result_to_str(result: ValidationResult[str]) -> str:
if result.is_valid:
# mypy understands result is Valid[str]
return result.val
else:
# mypy understands result is Invalid
err_type_cls = result.err_type.__class__.__name__
return (
f"Error of type {err_type_cls}, "
f"while validating {result.value} with {result.validator}"
)
Let’s see how it works
>>> validator = StringValidator()
>>> result_to_str(validator("abc123"))
'abc123'
>>> result_to_str(validator(0))
'Error of type TypeErr, while validating 0 with StringValidator()'
Pattern Matching¶
Pattern matching can make this more concise in Python 3.10+:
from koda_validate import ValidationResult, Valid, Invalid, IntValidator
def result_to_val(result: ValidationResult[str]) -> int | str:
match result:
case Valid(valid_val):
return valid_val
case Invalid(err_type, val, validator_):
return (
f"Error of type {err_type.__class__.__name__}, "
f"while validating {val} with {validator_}"
)
Let’s try it
>>> validator = IntValidator()
>>> result_to_val(validator(123))
123
>>> result_to_val(validator("abc"))
'Error of type TypeErr, while validating abc with IntValidator()'
ValidationResult.map()¶
Sometimes you might want to convert the data contained by Valid into another
type. .map allows you to do that without a lot of boilerplate:
>>> IntValidator()(5).map(str)
Valid(val='5')
Working with Invalid¶
Invalid instances provide machine-readable validation failure data.
In most cases you’ll want to transform these data in some way before sending it somewhere else. The expectation is that
built-in, or custom, utility functions should handle this. One such built-in function is to_serializable_errs. It
takes an Invalid instance and produces errors objects suitable for JSON / YAML serialization.
from koda_validate import StringValidator, Invalid
from koda_validate.serialization import to_serializable_errs
validator = StringValidator()
result = validator(123)
assert isinstance(result, Invalid)
print(to_serializable_errs(result))
Outputs
['expected a string']
Even if it doesn’t suit your ultimate purpose, to_serializable_errs can be useful during
development because the error messages tend to be more readable than the printed representation of
Invalid instances.
Note
to_serializable_errs is only meant to be a basic effort at a general English-language serializable
utility function. It may be convenient to work with, but please do not feel that you are in any way
limited to its functionality. Koda Validate’s intention is that users should be able to build whatever
error objects they need by consuming the Invalid data.