Using validators
Pankoff defines a small preset of validator, and it allow you to define your own.
Default validators
By default, Pankoff defines a few validators, String
, Number
, Type
, Sized
,
Predicate
, LazyLoad
. We’ll go over each one below.
- class pankoff.validators.String
Validate whether field is instance of type
str
.>>> @autoinit >>> class Person: ... name = String()
>>> person = Person(name="Guido")
- class pankoff.validators.List
Validate whether field is instance of type
list
.>>> @autoinit >>> class Person: ... items = List()
>>> person = Person(items=[1, 2, 3])
- class pankoff.validators.Dict(required_keys=None)
Validate whether field is instance of type
dict
.>>> @autoinit >>> class Person: ... mapping = Dict(required_keys=["name"])
>>> person = Person(mapping={"name": "Guido"})
- class pankoff.validators.Tuple
Validate whether field is instance of type
tuple
.>>> @autoinit >>> class Person: ... items = Tuple()
>>> person = Person(items=(1, 2, 3))
- class pankoff.validators.Iterable
Check whether field supports
collections.abc.Iterable
interface.
- class pankoff.validators.Container
Check whether field supports
collections.abc.Container
interface.
- class pankoff.validators.Hashable
Check whether field supports
collections.abc.Hashable
interface.
- class pankoff.validators.Iterator
Check whether field supports
collections.abc.Iterator
interface.
- class pankoff.validators.Reversible
Check whether field supports
collections.abc.Reversible
interface.
- class pankoff.validators.Generator
Check whether field supports
collections.abc.Generator
interface.
- class pankoff.validators.Callable
Check whether field supports
collections.abc.Callable
interface.
- class pankoff.validators.Collection
Check whether field supports
collections.abc.Collection
interface.
- class pankoff.validators.Sequence
Check whether field supports
collections.abc.Sequence
interface.
- class pankoff.validators.MutableSequence
Check whether field supports
collections.abc.MutableSequence
interface.
- class pankoff.validators.ByteString
Check whether field supports
collections.abc.ByteString
interface.
- class pankoff.validators.Set
Check whether field supports
collections.abc.Set
interface.
- class pankoff.validators.MutableSet
Check whether field supports
collections.abc.MutableSet
interface.
- class pankoff.validators.Mapping
Check whether field supports
collections.abc.Mapping
interface.
- class pankoff.validators.MutableMapping
Check whether field supports
collections.abc.MutableMapping
interface.
- class pankoff.validators.Awaitable
Check whether field supports
collections.abc.Awaitable
interface.
- class pankoff.validators.Coroutine
Check whether field supports
collections.abc.Coroutine
interface.
- class pankoff.validators.AsyncIterable
Check whether field supports
collections.abc.AsyncIterable
interface.
- class pankoff.validators.AsyncIterator
Check whether field supports
collections.abc.AsyncIterator
interface.
- class pankoff.validators.AsyncGenerator
Check whether field supports
collections.abc.AsyncGenerator
interface.
- class pankoff.validators.Number(mix_value=None, max_value=None)
Validate whether field is an instance of type
int
and within specified range.- Parameters
min_value (int, optional) – minimum value for a field
max_value (int, optional) – maximum value for a field
>>> @autoinit >>> class Person: ... age = Number(min_value=18, max_value=100)
>>> person = Person(age=25)
- class pankoff.validators.Type(types=(int, str, ...))
Validate whether field is instance of at least one type in
types
.>>> @autoinit >>> class Car: ... speed = Type(types=(int, float))
>>> car = Car(speed=500.4)
- class pankoff.validators.Sized(min_size=None, max_size=None)
Validate whether field length is within specified range.
- Parameters
min_size (int, optional) – minimum length for a field
max_size (int, optional) – maximum length for a field
>>> @autoinit >>> class Box: ... size = Sized(min_size=20, max_size=50)
>>> box = Box(size=40)
- class pankoff.validators.LazyLoad(factory)
Calculate an attribute based on other fields.
- Parameters
factory – callable to calculate value for current field, accepts current instance
>>> @autoinit >>> class Person(Container): ... name = String() ... surname = String() ... full_name = LazyLoad(factory=lambda instance: f"{instance.name} {instance.surname}")
>>> person = Person(name="Yaroslav", surname="Pankovych") >>> print(person) Person(name=Yaroslav, surname=Pankovych, full_name=Yaroslav Pankovych)
- class pankoff.validators.Predicate(predicate, default=None, error_message=None)
Predicate is a bit more complex validator. Basically, it checks your field against specified condition in
predicate
.predicate
is a simple callable which should return eitherTrue
orFalse
. It’ll be called with currentinstance
andvalue
to validate:predicate(instance, value)
.If
predicate
returnedFalse
, anddefault
is set,instance
andvalue
will be propagated todefault
ifdefault
is callable, if it’s not,default
will be returned straight away.THe key feature of
default
is that it can “normalize” your value if it’s invalid. See example below.- Parameters
predicate (callable) – function to call in order to validate value
default (callable, any, optional) – default value to use if
predicate
returnedFalse
>>> @autoinit >>> class Person: ... salary = Predicate( ... predicate=lambda instance, value: value == "100 USD", ... default=lambda instance, value: str(value) + " USD" ... )
>>> person = Person(salary=100) >>> person.salary "100 USD"
As you can see, we just turned
100
into"100 USD"
. You can also chain (see Chaining validators)Predicate
with other validators, and normalized value will be propagated to further validators.Predicate
supports rich error messages:>>> @autoinit >>> class Car: ... speed = Predicate( ... predicate=lambda instance, value: value == 100, ... error_message="Invalid value in field: {field_name}, got {value} for {predicate}" ... )
>>> car = Car(speed=50) Traceback (most recent call last): ... pankoff.exceptions.ValidationError: ['Invalid value in field: speed, got 50 for <lambda>']
Custom validators
You can define you ows validator by subclassing BaseValidator
.
>>> from pankoff.base import BaseValidator
>>> from pankoff.exceptions import ValidationError
>>> class EnumValidator(BaseValidator):
... def __setup__(self, allowed_values):
... self.allowed_values = allowed_values
... def mutate(self, instance, value):
... return f"Mutated value: {value}"
... def validate(self, instance, value):
... if value not in self.allowed_values:
... raise ValidationError(
... f"Invalid value in field {self.field_name}, value should be in {self.allowed_values} "
... f"got {value}"
... )
It is required for validators to define validate
, but __setup__
and mutate is optional.
You can use mutate
to modify returned value when its being accessed. It won’t be cached, mutate
is re-calculated on every
attribute access.