def test_no_type_check(self): """ Verifies that no_type_check is respected """ def get_sample_func(): def sample(data: int): pass return sample sample_d1 = typing.no_type_check(runtime_validation(get_sample_func())) sample_d2 = runtime_validation(typing.no_type_check(get_sample_func())) sample_d3 = runtime_validation(get_sample_func()) sample_d4 = typing.no_type_check_decorator( runtime_validation(get_sample_func()) ) sample_d5 = runtime_validation( typing.no_type_check_decorator(get_sample_func()) ) get_sample_func()("str") sample_d1("str") sample_d2("str") with self.assertRaises(RuntimeTypeError): sample_d3("str")
def test_named_tuple(self): from collections import namedtuple MyNamedTuple = typing.NamedTuple("MyNamedTuple", [("my_int", int)]) t = MyNamedTuple(5) t1 = namedtuple("MyNamedTuple", "my_int")(5) t2 = namedtuple("MyNamedTuple", "my_int")("string") t3 = runtime_validation(MyNamedTuple)(5) t4 = (5,) t5 = "5" @runtime_validation def sample(data: MyNamedTuple) -> MyNamedTuple: return data # invariant cases sample(t) with self.assertRaises(RuntimeTypeError): sample(t1) with self.assertRaises(RuntimeTypeError): sample(t2) sample(t3) with self.assertRaises(RuntimeTypeError): sample(t4) with self.assertRaises(RuntimeTypeError): sample(t5) # Covariant case config({"mode": "covariant"})
def get_type_var_func(self, configurable=False, type_var=None): if type_var is None: A = typing.TypeVar('A') else: A = type_var def type_var_func(data: A) -> A: return data def configurable_type_var_func(data: typing.Any, type_option: A) -> A: return data if configurable: return runtime_validation(configurable_type_var_func) else: return runtime_validation(type_var_func)
def get_type_var_func(self, configurable=False, type_var=None): if type_var is None: A = typing.TypeVar("A") else: A = type_var def type_var_func(data: A) -> A: return data def configurable_type_var_func(data: typing.Any, type_option: A) -> A: return data if configurable: return runtime_validation(configurable_type_var_func) else: return runtime_validation(type_var_func)
def test_named_tuple(self): from collections import namedtuple MyNamedTuple = typing.NamedTuple('MyNamedTuple', [('my_int', int)]) t = MyNamedTuple(5) t1 = namedtuple('MyNamedTuple', 'my_int')(5) t2 = namedtuple('MyNamedTuple', 'my_int')('string') t3 = runtime_validation(MyNamedTuple)(5) t4 = (5, ) t5 = '5' @runtime_validation def sample(data: MyNamedTuple) -> MyNamedTuple: return data # invariant cases sample(t) with self.assertRaises(RuntimeTypeError): sample(t1) with self.assertRaises(RuntimeTypeError): sample(t2) sample(t3) with self.assertRaises(RuntimeTypeError): sample(t4) with self.assertRaises(RuntimeTypeError): sample(t5) # Covariant case config({'mode': 'covariant'})
def decorate(self, f): wrapper = enforce.runtime_validation(f) wrapper = functools.update_wrapper(wrapper, f) name = f.__name__ self[f.__qualname__] = wrapper if name in self: # There is a conflict in short name log.debug("selector %s already registered %s vs %s", name, f.__qualname__, self[name].__qualname__) else: self[name] = wrapper return wrapper
def test_no_type_check(self): """ Verifies that no_type_check is respected """ def get_sample_func(): def sample(data: int): pass return sample sample_d1 = typing.no_type_check(runtime_validation(get_sample_func())) sample_d2 = runtime_validation(typing.no_type_check(get_sample_func())) sample_d3 = runtime_validation(get_sample_func()) sample_d4 = typing.no_type_check_decorator(runtime_validation(get_sample_func())) sample_d5 = runtime_validation(typing.no_type_check_decorator(get_sample_func())) get_sample_func()('str') sample_d1('str') sample_d2('str') with self.assertRaises(RuntimeTypeError): sample_d3('str')
def exec_module(self, module: ModuleType) -> None: from enforce import runtime_validation super().exec_module(module) if module.__package__ and module.__package__.split( '.')[0] in check_mods: for clsname, cls in inspect.getmembers(module, inspect.isclass): for funname, fun in list((k, v) for k, v in cls.__dict__.items() if inspect.isfunction(v)): try: module.__dict__[funname] = runtime_validation(fun) except Exception as e: logging.debug( f'failed to apply runtime validation on {fun}: {e}' )
def test_docstring_name_presrved(self): """ Verifies that an original name and a docstring are preserved """ def test(text: str) -> None: """I am a docstring""" print(text) original_name = test.__name__ original_doc = test.__doc__ test = runtime_validation(test) self.assertEqual(original_doc, test.__doc__) self.assertEqual(original_name, test.__name__)
def test_validate_none_enforce(): """ Tests that a None will be caught by enforce: no need for not_none validator """ from enforce import runtime_validation, config from enforce.exceptions import RuntimeTypeError # we're not supposed to do that but if your python environment is a bit clunky, that might help config(dict(mode='covariant')) from ._test_pep484 import create_for_test_validate_none_enforce myfunc = create_for_test_validate_none_enforce() # decorate manually (reverse order) myfunc = validate_io(a=[is_even, gt(1)], b=is_even, c=is_even)(myfunc) myfunc = runtime_validation(myfunc) # -- check that the validation works myfunc(84, None) # OK because b is Nonable and c is optional with default value None with pytest.raises(RuntimeTypeError): myfunc(None, 0) # RuntimeTypeError: a is None
def test_custom_generic_initialisation(self): """ Verifies that user defined generics can be initialised """ T = typing.TypeVar('T') class Sample(typing.Generic[T]): pass SD = runtime_validation(Sample) ST = Sample[int] SDT = SD[int] s = Sample() sd = SD() st = ST() sdt = SDT() self.assertFalse(hasattr(s, '__enforcer__')) self.assertFalse(hasattr(st, '__enforcer__')) self.assertTrue(hasattr(sd, '__enforcer__')) self.assertTrue(hasattr(sdt, '__enforcer__')) self.assertEqual(sd.__enforcer__.signature, Sample) self.assertEqual(sdt.__enforcer__.signature, Sample) self.assertEqual(sd.__enforcer__.generic, SD.__enforcer__.generic) self.assertEqual(sdt.__enforcer__.generic, SDT.__enforcer__.generic) self.assertEqual(sd.__enforcer__.bound, SD.__enforcer__.bound) self.assertEqual(sdt.__enforcer__.bound, SDT.__enforcer__.bound) for hint_name, hint_value in sdt.__enforcer__.hints.items(): self.assertEqual(hint_value, SDT.__enforcer__.hints[hint_name]) self.assertEqual(len(sdt.__enforcer__.hints), len(SDT.__enforcer__.hints))
def test_typed_named_tuple(self): MyNamedTuple = typing.NamedTuple("MyNamedTuple", [("my_int", int)]) MyNamedTuple = runtime_validation(MyNamedTuple) mt = MyNamedTuple(5) mt2 = MyNamedTuple(my_int=5) self.assertEqual(mt[0], 5) self.assertEqual(mt.my_int, 5) self.assertEqual(mt2[0], 5) self.assertEqual(mt2.my_int, 5) with self.assertRaises(RuntimeTypeError): MyNamedTuple("string") with self.assertRaises(RuntimeTypeError): MyNamedTuple(my_int="string") with self.assertRaises(AttributeError): mt2.my_int = "hello world" with self.assertRaises(TypeError): MyNamedTuple(2, my_int="string")
def test_typed_named_tuple(self): MyNamedTuple = typing.NamedTuple('MyNamedTuple', [('my_int', int)]) MyNamedTuple = runtime_validation(MyNamedTuple) mt = MyNamedTuple(5) mt2 = MyNamedTuple(my_int=5) self.assertEqual(mt[0], 5) self.assertEqual(mt.my_int, 5) self.assertEqual(mt2[0], 5) self.assertEqual(mt2.my_int, 5) with self.assertRaises(RuntimeTypeError): MyNamedTuple('string') with self.assertRaises(RuntimeTypeError): MyNamedTuple(my_int='string') with self.assertRaises(AttributeError): mt2.my_int = 'hello world' with self.assertRaises(TypeError): MyNamedTuple(2, my_int='string')