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_type
for type-only, andhas_valid_value
for 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
vtype
argument:
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_type
andhas_valid_value
methods 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
valid8
library ?) - 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 thevalid8
syntax 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