Validation Results#

In Koda Validate, Validators express validation success or failure by returning a ValidationResult. To be more specific it requires on 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] can be de-sugared to Union[Valid[int], Invalid].

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:


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.