Example 4
4- l
is a list of custom tuples¶
(a) Inline - validate
¶
It is not possible to use validate
to check all the tuples inside l
at once, but it can be used in a for
loop to validate each tuple. Note that we correctly set the name of each item that is validated, so that users get informative errors:
from valid8 import validate
# first validate the main type
validate('l', l, instance_of=list)
# then validate (and use) the contents
for i, v in enumerate(l):
# each item is a tuple of size 2
validate('l[{}]'.format(i), l[i], instance_of=tuple, length=2)
# the first element is a float between 0 and 1
validate('l[{}][0]'.format(i), l[i][0], instance_of=Real, min_value=0,
max_value=1)
# the second element is a lowercase string of size 3
validate('l[{}][1]'.format(i), l[i][1], instance_of=str, length=3,
equals=l[i][1].lower())
# here you can actually USE the current item
(b) Inline - with validator
¶
You can perform the whole validation at once with validator
as shown here:
from valid8 import validator, instance_of
with validator('l', l, instance_of=list) as v:
v.alid = all(
# each item is a tuple of size 2
instance_of(item, tuple) and len(item) == 2
# the first element is a float between 0 and 1
and instance_of(item[0], Real) and (0 <= item[0] <= 1)
# the second element is a lowercase string of size 3
and instance_of(item[1], str) and len(item[1]) == 3 and item[1].islower()
for item in l)
But it might not really make sense if you actually wish to use the tuples :). In that case it is much more readable and convenient to write a custom validation function check_valid_tuple
first. Below is an example in "failure raiser" mode: the function does not return False in case of failure but rather raises more informative errors.
def check_valid_tuple(tup):
""" custom validation function - here in 'failure raiser' style (returning nothing) """
# each item is a tuple of size 2
if not isinstance(tup, tuple):
raise TypeError('item should be a tuple')
if len(tup) != 2:
raise ValueError('tuple length should be 2')
# the first element is a float between 0 and 1
if not isinstance(tup[0], Real):
raise TypeError('first element should be a Real')
if not (0 <= tup[0] <= 1):
raise ValueError('first element should be between 0 and 1')
# the second element is a lowercase string of size 3
if not isinstance(tup[1], str):
raise TypeError('second element should be a string')
if not (len(tup[1]) == 3 and tup[1].islower()):
raise ValueError('second element should be a lowercase string of length 3')
We can then use it with a validation
context manager:
from valid8 import validate, validation
# first validate the main type
validate('l', l, instance_of=list)
# then validate (and use) the contents
for i, v in enumerate(l):
# each item is a valid tuple
with validation('l[{}]'.format(i), l[i]):
check_valid_tuple(l[i])
# here you can actually USE the current item
Note that if you choose to use a PEP484 type checker such as pytypes, your custom validation function can be much more compact, as the tuple length and tuple elements types are described in the compact PEP484 type hint Tuple[Real, str]
:
from typing import Tuple
from pytypes import typechecked
@typechecked
def check_valid_tuple(tup: Tuple[Real, str]):
""" custom validation function - note the PEP484 type hint above """
# the first element is a float between 0 and 1
if not (0 <= tup[0] <= 1):
raise ValueError('first element should be between 0 and 1')
# the second element is a lowercase string of size 3
if not (len(tup[1]) == 3 and tup[1].islower()):
raise ValueError('second element should be a lowercase string of length 3')
(c) Decorators - built-in lib¶
For readability we only show one decorator here, see this previous example for other decorators and additional comments.
There is no built-in function to check that l[i][1]
is lowercase yet.
The best we can do is
from valid8 import validate_arg, instance_of, on_all_, on_each_, has_length, and_, between
@validate_arg('l', instance_of(list), on_all_(
# each item is a tuple of size 2
instance_of(tuple), has_length(2),
on_each_(
# the first element is a float between 0 and 1
and_(instance_of(Real), between(0, 1)),
# the 2d element is a string of len 3 BUT we cannot check lowercase
and_(instance_of(str), has_length(3)),
)
))
def my_function(l):
pass
Although this proves that the provided built-in library can tackle complex cases, it also shows the limits of performing all validation directly in the decorator. In this case it is much more readable to create a custom function is_valid_tuple
, and it allows you to actually check the lowercase part:
def is_valid_tuple(t):
""" Custom validation function. We could also provide a callable """
# (a) each item is a tuple of size 2
# --you can reuse an entire method from the built-in lib when it supports direct calling mode
instance_of(t, tuple)
# --otherwise you can reuse a failure class, there are many
if len(t) != 2: raise WrongLength(t, ref_length=2)
# (b) the first element is a float between 0 and 1
if not isinstance(t[0], Real): raise HasWrongType(t[0], Real)
if not (0 <= t[0] <= 1): raise NotInRange(t[0], min_value=0, max_value=1)
# (c) the second element is a lowercase string of size 3
instance_of(t[1], str)
if len(t[1]) != 3: raise WrongLength(t[1], ref_length=3)
ValidationFailurelure types
if not t[1].islower():
raise NotLowerCase(t[1])
class NotLowerCase(ValidationFailurValidationFailurenisms than ValidationError)"""
help_msg = "Value is not a lowercase string: {wrong_value}"
And finally you would use your custom function in the decorator:
@validate_arg('l', instance_of(list), on_all_(is_valid_tuple))
def my_function(l):
pass
(d) Decorators - mini_lambda¶
For readability we only show one decorator here, see this previous example for other decorators and additional comments.
from valid8 import validate_arg, instance_of, on_all_
# just for fun: we create our custom mini_lambda variable named 't'
from mini_lambda import InputVar, Len, Isinstance
t = InputVar('t', tuple)
@validate_arg('l', instance_of(list), on_all_(
# each item is a tuple of size 2
instance_of(tuple), Len(t) == 2,
# the first element is a float between 0 and 1
Isinstance(t[0], Real), (0 <= t[0]) & (t[0] <= 1),
# the 2d element is a lowercase string of len 3
Isinstance(t[1], str), Len(t[1]) == 3, t[1].islower()
))
def my_function(l):
pass