def class_field_mini_lambda(value): from mini_lambda import InputVar, Len from valid8 import validate_field from valid8.validation_lib import instance_of from valid8.validation_lib.mini_lambda_ import Instance_of # just for fun: we create our custom mini_lambda variable named 't' t = InputVar('t', tuple) @validate_field( 't', instance_of(tuple), Len(t) == 2, # the first element is a float between 0 and 1 Instance_of(t[0], Real), (0 <= t[0]) & (t[0] <= 1), # the 2d element is a lowercase string of len 3 Instance_of(t[1], str), Len(t[1]) == 3, t[1].islower()) class Foo: def __init__(self, t): self.s = t Foo(value)
def test_evaluator_complex_1(): """ A complex case with a combination of Repr, Len, and comparisons """ s = InputVar('s', str) reasonable_string = Repr((2 <= Len(s)) & (Len(s) < 3)) reasonable_string = reasonable_string.as_function() assert reasonable_string('r') == 'False'
def test_validate_mini_lambda(): """ Tests that mini_lambda works with @validate_io """ from mini_lambda import Len, s, x, Int @validate_io(name=(0 < Len(s)) & (Len(s) <= 10), age=(x > 0) & (Int(x) == x)) def hello_world(name, age): print('Hello, ' + name + ' your age is ' + str(age)) hello_world('john', 20)
def test_evaluator_sized(): """ Sized Container Object: tests that len() raises the appropriate error but that the equivalent Len() works """ s = InputVar('s', str) with pytest.raises(FunctionDefinitionError): len(s) string_length = Len(s) string_length = string_length.as_function() assert string_length('tho') == 3
def test_readme_index_usage_function(): """ Tests that the examples provided in the index page under Usage examples/Function are correct """ from mini_lambda import s, Len from valid8 import validate_arg from valid8.validation_lib import instance_of @validate_arg('name', instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') def build_house(name, surface=None): print('Building house... DONE !') return name, surface build_house('sweet home', 200) with pytest.raises(InputValidationError) as exc_info: build_house('', 200) # name is invalid e = exc_info.value assert str(e) == "name should be a non-empty string. " \ "Error validating input [name=''] for function [build_house]. " \ "At least one validation function failed for value ''. " \ "Successes: [\"instance_of_%r\"] / Failures: {'len(s) > 0': 'Returned False.'}." \ "" % str from mini_lambda import s, x, l, Len from valid8 import validate_arg, validate_out from valid8.validation_lib import instance_of, is_multiple_of @validate_arg('name', instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') @validate_arg( 'surface', (x >= 0) & (x < 10000), is_multiple_of(100), help_msg='Surface should be a multiple of 100 between 0 and 10000.') @validate_out(instance_of(tuple), Len(l) == 2) def build_house(name, surface=None): print('Building house... DONE !') return name, surface build_house('sweet home') build_house('sweet home', None) # No error ! with pytest.raises(TypeError): is_multiple_of(100)(None) with pytest.raises(TypeError): (Len(s) > 0).evaluate(None)
class House: @validate_arg('name', Len(s) > 0, error_type=InvalidName) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurface) def __init__(self, name: str, surface: Integral = None): pass
def test_readme_index_combining_autoclass(): """ Tests that the examples provided in the index page under Combining/autoclass are correct """ from autoclass import autoclass from mini_lambda import s, x, Len from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of class InvalidNameError(ClassFieldValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(ClassFieldValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' @validate_field('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_field('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) @autoclass class House(object): def __init__(self, name, surface=None): pass h = House('sweet home', 200) h.surface = None # Valid (surface is nonable by signature) with pytest.raises(InvalidNameError): h.name = '' # InvalidNameError with pytest.raises(InvalidSurfaceError): h.surface = 10000 # InvalidSurfaceError
class House(object): @validate_arg('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) def __init__(self, name, surface=None): pass
def test_evaluator_sized_compared(): """ Tests that Len(s) > 2 works as well as (2 <= Len(s)) & (Len(s) < 3)""" s = InputVar('s', str) big_string = Len(s) > 2 big_string = big_string.as_function() assert big_string('tho') assert not big_string('r') reasonable_string = L((2 <= Len(s)) & (Len(s) < 3)) # reasonable_string = reasonable_string.as_function() assert reasonable_string('th') assert not reasonable_string('r') assert not reasonable_string('rats')
def ex2_pep484(value): from mini_lambda import s, Len @typechecked @validate_arg('s', Len(s) > 0, s.islower()) def my_function(s: str): # <- type hint in signature pass my_function(value)
def test_testing(): """ """ from mini_lambda import s, Len from valid8 import assert_valid, Validator from valid8.validation_lib import instance_of name = 'sweet_home' assert_valid('name', name, instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') v = Validator(instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') v.assert_valid('name', name)
def function_input_mini_lambda(value): from mini_lambda import s, Len from valid8 import validate_arg from valid8.validation_lib import instance_of @validate_arg('s', instance_of(str), Len(s) > 0, s.islower()) def my_function(s): pass my_function(value)
def class_field_mini_lambda(value): from mini_lambda import s, Len from valid8 import validate_field from valid8.validation_lib import instance_of @validate_field('s', instance_of(str), Len(s) > 0, s.islower()) class Foo: def __init__(self, s): self.s = s Foo(value)
def function_input_mini_lambda(value): from mini_lambda import InputVar, Len from valid8 import validate_arg from valid8.validation_lib import instance_of from valid8.validation_lib.mini_lambda_ import Instance_of # just for fun: we create our custom mini_lambda variable named 't' t = InputVar('t', tuple) @validate_arg( 't', instance_of(tuple), Len(t) == 2, # the first element is a float between 0 and 1 Instance_of(t[0], Real), (0 <= t[0]) & (t[0] <= 1), # the 2d element is a lowercase string of len 3 Instance_of(t[1], str), Len(t[1]) == 3, t[1].islower()) def my_function(t): pass my_function(value)
def ex3_pep484(value): from mini_lambda import InputVar, Len # we need a mini_lambda variable named 't' t = InputVar('t', tuple) @typechecked @validate_arg( 't', # the first element is a float between 0 and 1 (0 <= t[0]) & (t[0] <= 1), # the 2d element is a lowercase string of len 3 Len(t[1]) == 3, t[1].islower()) def my_function(t: Tuple[Real, str]): pass my_function(value)
def test_readme_combining_enforce(): # Imports - for type validation from numbers import Integral from typing import Tuple, Optional from enforce import runtime_validation, config config(dict(mode='covariant') ) # means that subclasses of required types are valid too from enforce.exceptions import RuntimeTypeError # Imports - for value validation from mini_lambda import s, x, Len from valid8 import validate_arg from valid8.validation_lib import is_multiple_of # Define our 2 applicative error types class InvalidNameError(InputValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(InputValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' # Apply type + value validation @runtime_validation @validate_arg('name', Len(s) > 0, error_type=InvalidNameError) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) def build_house( name: str, surface: Optional[Integral] = None ) -> Tuple[str, Optional[Integral]]: print('Building house... DONE !') return name, surface build_house('sweet home', 200) build_house('sweet home') with pytest.raises(InvalidNameError): build_house('', 100) # InvalidNameError with pytest.raises(InvalidSurfaceError): build_house('sweet home', 10000) # InvalidSurfaceError with pytest.raises(RuntimeTypeError): build_house('test', 100.1) # RuntimeTypeError
def test_validate_field_custom_type(): """""" from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of from mini_lambda import x, s, Len class InvalidNameError(ClassFieldValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(ClassFieldValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' @validate_field('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_field('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) class House(object): """Properties work only with new-style classes""" def __init__(self, name, surface=None): self.name = name self.surface = surface @property def surface(self): return self.__surface @surface.setter def surface(self, surface=None): self.__surface = surface h = House('sweet home') h.name = '' # DOES NOT RAISE InvalidNameError with pytest.raises(InvalidNameError): h = House('') h.surface = 100 with pytest.raises(InvalidSurfaceError): h.surface = 10000
def test_readme_index_usage_class_fields(): """ Tests that the examples provided in the index page under Usage examples/class fields are correct""" from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of from mini_lambda import x, s, Len @validate_field('name', instance_of(str), Len(s) > 0, help_msg='name should be a non-empty string') @validate_field( 'surface', (x >= 0) & (x < 10000), is_multiple_of(100), help_msg='Surface should be a multiple of 100 between 0 and 10000.') class House(object): def __init__(self, name, surface=None): self.name = name self.surface = surface @property def surface(self): return self.__surface @surface.setter def surface(self, surface=None): self.__surface = surface h = House('sweet home') h.name = '' # DOES NOT RAISE InvalidNameError with pytest.raises(ClassFieldValidationError): h = House('') h.surface = 100 with pytest.raises(ClassFieldValidationError): h.surface = 10000
def test_readme_index_combining_attrs(): """ Tests that the examples provided in the index page under Combining/autoclass are correct """ import attr from mini_lambda import s, x, Len from valid8 import validate_field, ClassFieldValidationError from valid8.validation_lib import instance_of, is_multiple_of class InvalidNameError(ClassFieldValidationError): help_msg = 'name should be a non-empty string' class InvalidSurfaceError(ClassFieldValidationError): help_msg = 'Surface should be a multiple of 100 between 0 and 10000.' @validate_field('name', instance_of(str), Len(s) > 0, error_type=InvalidNameError) @validate_field('surface', (x >= 0) & (x < 10000), is_multiple_of(100), error_type=InvalidSurfaceError) @attr.s class House(object): name = attr.ib() surface = attr.ib(default=None) h = House( 'sweet home') # Valid (surface is nonable by generated signature) h.name = '' # DOES NOT RAISE InvalidNameError (no setter!) with pytest.raises(InvalidNameError): House('', 10000) # InvalidNameError with pytest.raises(InvalidSurfaceError): House('sweet home', 10000) # InvalidSurfaceError
def test_readme_examples_4(): """ Tests that the example 4 provided in the documentation works (list of custom tuples) """ l = [(1, 'ras'), (0.2, 'abc')] # ---- inline 1 from valid8 import validate # first validate the main type validate('l', l, instance_of=list) for i, v in enumerate(l): # each item is a tuple of size 2 validate('l[%s]' % i, l[i], instance_of=tuple, length=2) # the first element is a float between 0 and 1 validate('l[%s][0]' % 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[%s][1]' % i, l[i][1], instance_of=str, length=3, equals=l[i][1].lower()) # ---- inline 2 from valid8 import validator from valid8.validation_lib import instance_of l = [(1, 'ras'), (0.2, 'abc')] # all at once 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) # custom validation function 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') 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[%s]' % i, l[i]): check_valid_tuple(l[i]) # here you can actually USE the current item # ---- function input from valid8 import validate_arg, and_ from valid8.validation_lib import instance_of, on_all_, on_each_, has_length, between @validate_arg( 'l', instance_of(list), on_all_( instance_of(tuple), has_length(2), # each item is a tuple of size 2 on_each_( and_(instance_of(Real), between( 0, 1)), # the first element is a float between 0 and 1 and_( instance_of(str), has_length(3) ), # the 2d element is a string of len 3 BUT we cannot check lowercase ))) def my_function(l): pass l = [(1, 'ras'), (0.2, 'aBc')] # we cannot check lowercase my_function(l) # much better: from valid8 import validate_arg, ValidationFailure from valid8.validation_lib import instance_of, on_all_, HasWrongType, WrongLength, NotInRange 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) # -- finally you can write custom ValidationFailure types if not t[1].islower(): raise NotLowerCase(t[1]) class NotLowerCase(ValidationFailure, ValueError): """ Example custom exception class used in custom validation function. `ValidationFailure` base class provides some mechanisms to easily build the help message (same mechanisms than ValidationError)""" help_msg = "Value is not a lowercase string: {wrong_value}" @validate_arg('l', instance_of(list), on_all_(is_valid_tuple)) def my_function(l): pass l = [(1, 'ras'), (0.2, 'abc')] my_function(l) # ---- mini_lambda from valid8 import validate_arg from valid8.validation_lib import 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 l = [(1, 'ras'), (0.2, 'abc')] my_function(l)
class House: @validate_arg('name', Len(s) > 0) @validate_arg('surface', (x >= 0) & (x < 10000), is_multiple_of(100)) def __init__(self, name: str, surface: int = 100): pass
def test_doc_usage_syntax_2(): """ Tests that the second example in doc/usage in the syntax section works """ from mini_lambda import b, i, s, l, x from mini_lambda import Slice, Get, Not, In from mini_lambda import Iter, Repr, Format, Len, Int, Any, Log, DDecimal from math import log from decimal import Decimal # boolean logic with pytest.raises(FunctionDefinitionError): expr = (x > 1) and (x < 5) # fails expr = (x > 1) & (x < 5) # OK # iterating with pytest.raises(FunctionDefinitionError): expr = next(iter(s)) # fails expr = next(Iter(s)) # OK # calling with the variable as arg with pytest.raises(FunctionDefinitionError): expr = log(x) # fails expr = Log(x) # OK # constructing with the variable as arg with pytest.raises(TypeError): expr = Decimal(x) # fails expr = DDecimal(x) # OK # getting with the variable as the key with pytest.raises(FunctionDefinitionError): expr = {'a': 1}[s] # fails expr = Get({'a': 1}, s) # OK # slicing with the variable as index with pytest.raises(FunctionDefinitionError): expr = 'hello'[0:i] # fails expr = Get('hello', Slice(0, i)) # OK # representing: Repr/Str/Bytes/Sizeof/Hash with pytest.raises(FunctionDefinitionError): expr = repr(l) # fails expr = Repr(l) # OK # formatting with the variable in the args with pytest.raises(FunctionDefinitionError): expr = '{} {}'.format(s, s) # fails expr = Format('{} {}', s, s) # OK # sizing with pytest.raises(FunctionDefinitionError): expr = len(l) # fails expr = Len(l) # OK # casting (Bool, Int, Float, Complex, Hex, Oct) with pytest.raises(FunctionDefinitionError): expr = int(s) # fails expr = Int(s) # OK # not with pytest.raises(FunctionDefinitionError): expr = not b # fails expr = b.not_() # OK expr = Not(b) # OK # any/all with pytest.raises(FunctionDefinitionError): expr = any(l) # fails expr = l.any_() # OK expr = Any(l) # OK # membership testing (variable as container) with pytest.raises(FunctionDefinitionError): expr = 'f' in l # fails expr = l.contains('f') # OK expr = In('f', l) # OK # membership testing (variable as item) with pytest.raises(FunctionDefinitionError): expr = x in [1, 2] # fails expr = x.is_in([1, 2]) # OK expr = In(x, [1, 2]) # OK with pytest.raises(FunctionDefinitionError): expr = 0 < x < 1 # chained comparisons (use parenthesis and & instead) with pytest.raises(FunctionDefinitionError): expr = [i for i in l ] # list/tuple/set/dict comprehensions (no workaround)