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.Iterableinterface.
- class pankoff.validators.Container
Check whether field supports
collections.abc.Containerinterface.
- class pankoff.validators.Hashable
Check whether field supports
collections.abc.Hashableinterface.
- class pankoff.validators.Iterator
Check whether field supports
collections.abc.Iteratorinterface.
- class pankoff.validators.Reversible
Check whether field supports
collections.abc.Reversibleinterface.
- class pankoff.validators.Generator
Check whether field supports
collections.abc.Generatorinterface.
- class pankoff.validators.Callable
Check whether field supports
collections.abc.Callableinterface.
- class pankoff.validators.Collection
Check whether field supports
collections.abc.Collectioninterface.
- class pankoff.validators.Sequence
Check whether field supports
collections.abc.Sequenceinterface.
- class pankoff.validators.MutableSequence
Check whether field supports
collections.abc.MutableSequenceinterface.
- class pankoff.validators.ByteString
Check whether field supports
collections.abc.ByteStringinterface.
- class pankoff.validators.Set
Check whether field supports
collections.abc.Setinterface.
- class pankoff.validators.MutableSet
Check whether field supports
collections.abc.MutableSetinterface.
- class pankoff.validators.Mapping
Check whether field supports
collections.abc.Mappinginterface.
- class pankoff.validators.MutableMapping
Check whether field supports
collections.abc.MutableMappinginterface.
- class pankoff.validators.Awaitable
Check whether field supports
collections.abc.Awaitableinterface.
- class pankoff.validators.Coroutine
Check whether field supports
collections.abc.Coroutineinterface.
- class pankoff.validators.AsyncIterable
Check whether field supports
collections.abc.AsyncIterableinterface.
- class pankoff.validators.AsyncIterator
Check whether field supports
collections.abc.AsyncIteratorinterface.
- class pankoff.validators.AsyncGenerator
Check whether field supports
collections.abc.AsyncGeneratorinterface.
- class pankoff.validators.Number(mix_value=None, max_value=None)
Validate whether field is an instance of type
intand 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.predicateis a simple callable which should return eitherTrueorFalse. It’ll be called with currentinstanceandvalueto validate:predicate(instance, value).If
predicatereturnedFalse, anddefaultis set,instanceandvaluewill be propagated todefaultifdefaultis callable, if it’s not,defaultwill be returned straight away.THe key feature of
defaultis 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
predicatereturnedFalse
>>> @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
100into"100 USD". You can also chain (see Chaining validators)Predicatewith other validators, and normalized value will be propagated to further validators.Predicatesupports 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.