vtypes - validating types¶
Validating types for python - use isinstance() to validate both type and value.
vtypes is a small library to define "validating types". These types can be used to add value validation on top of type checking anywhere where you usually rely on isinstance(). This can in particular be used to make validation schemas simpler and more readable, for example used in pyfields.
Installing¶
> pip install vtypes
Usage¶
a - basics¶
A VType is a combination of :
- a type name, for example
'PositiveInt' - one or several base types: for example
int. When several are provided, they all should match ("and" combination). - one or several validators: for example
lambda x: x >= 0
A VType is constructed using vtype(name, type, validators, ...). For example we can create a positive int:
from vtypes import vtype
PositiveInt = vtype('PositiveInt', int, {'should be positive': lambda x: x >= 0})
A VType's main purpose is to behave like a type (therefore to be compliant with isinstance) and to validate both type and values at the same time when isinstance is called:
assert isinstance(1, PositiveInt)
assert not isinstance(-1, PositiveInt) # an int, but not >= 0
An alternate way to define a VType is to define a python class inheriting from VType.
-
base types can be either provided as superclass, or in the
__type__class attribute, -
validators can be provided in the
__validators__class attribute.
So the following two classes are equivalent to our previous PositiveInt example:
from vtypes import VType
# type is provided in the ancestors
class PositiveInt(int, VType):
__validators__ = {'should be positive': lambda x: x >= 0}
# type is provided in __type__
class PositiveInt2(VType):
__type__ = int
__validators__ = {'should be positive': lambda x: x >= 0}
b - goodies¶
In addition to this primary feature, a VType provides a few handy methods:
- detailed error messages with
validate(note: a variable name should be provided, for example'size').
>>> PositiveInt.validate('size', -1)
ValidationError[ValueError]: Error validating [size=-1].
InvalidValue: should be positive.
Function [<lambda>] returned [False] for value -1.
- partial checkers:
has_valid_typefor type-only, andhas_valid_valuefor value-only:
assert PositiveInt.has_valid_type(-1) # -1 is an int
assert not PositiveInt.has_valid_value(-1) # -1 < 0
Finally, you may wish to use is_vtype to check if anything is a VType:
from vtypes import is_vtype
assert is_vtype(PositiveInt)
assert not is_vtype(int)
assert not is_vtype(1)
c - validators syntax¶
There are many ways to declare validators:
-
a single callable
-
a single tuple
(<callable>, <error_msg>),(<callable>, <failure_type>)or(<callable>, <error_msg>, <failure_type>) -
a list of such callables and tuples
-
a dictionary where keys are
<callable>,<error_msg>, or<failure_type>and values are one or two (tuple) of such elements. This is at least for the author, the most intuitive and readable style:
ConstrainedInt = vtype('ConstrainedInt', int,
{'should be positive': lambda x: x >= 0,
'should be a multiple of 3': lambda x: x % 3})
Note that this syntax is valid8 simple syntax.
If you wish to create even more compact callables, you may wish to look at mini_lambda.
d - composition¶
You can combine types, for example a nonempty string can be obtained by mixing NonEmpty and str.
- with the compact style: simply put the classes to combine in a tuple in the second
vtypeargument:
NonEmpty = vtype('NonEmpty', (), {'should be non empty': lambda x: len(x) > 0})
"""A VType describing non-empty containers, with strictly positive length."""
NonEmptyStr = vtype('NonEmptyStr', (NonEmpty, str), ())
"""A VType for non-empty strings"""
- with the class style: either use inheritance, or fill the
__type__class attribute with a tuple
class NonEmpty(VType):
"""A VType describing non-empty containers, with strictly positive length."""
__validators__ = {'should be non empty': lambda x: len(x) > 0}
class NonEmptyStr(NonEmpty, str):
"""A VType for non-empty strings"""
class NonEmptyStr2(VType):
"""A VType for non-empty strings - alternate style"""
__type__ = NonEmpty, str
All these behave as expected:
assert isinstance('hoho', NonEmptyStr)
assert not isinstance('', NonEmptyStr)
assert not isinstance(1, NonEmptyStr)
Main features¶
- Validate both type and value with
isinstance, thanks to easy-to-write "validating types" has_valid_typeandhas_valid_valuemethods provided for easy auditing, as well asis_vtype- Validation syntax fully compliant with
valid8. Compliant error message available through avalidate()method - v-types are composable so that creating a library of reusable elements is straightforward (note: should we provide one in this library based on
valid8library ?) - Two styles:
vtype(...)constructor method, as well as an alternateclass ...(VType)style to perform composition using inheritance, and write docstrings more easily.
See Also¶
checktypes, that was a great source of inspiration. The only reason I ended up recreating something new a couple years after discovering it, was that I really wanted to leverage thevalid8syntax for validators (as well as its standardized exceptions).
Do you like this library ? You might also like my other python libraries
Want to contribute ?¶
Details on the github page: https://github.com/smarie/python-vtypes