def validate( obj: T, validators: Iterable[Validator] = None, kwargs: Mapping[str, Any] = None, *, aliaser: Aliaser = lambda s: s, ) -> T: if validators is None: validators = get_validators(type(obj)) error: Optional[ValidationError] = None validators = iter(validators) for validator in validators: try: if not kwargs: validator.validate(obj) elif validator.params == kwargs.keys(): validator.validate(obj, **kwargs) else: if any(k not in kwargs for k in validator.params): raise RuntimeError( f"Missing parameters {kwargs.keys() - validator.params}" f" for validator {validator.func}") validator.validate(obj, **{k: kwargs[k] for k in validator.params}) except ValidationError as e: err = apply_aliaser(e, aliaser) except NonTrivialDependency as exc: exc.validator = validator raise except AssertionError: raise except Exception as e: err = ValidationError([str(e)]) else: continue if validator.field is not None: alias = getattr(get_alias(validator.owner), get_field_name(validator.field)) err = ValidationError(children={aliaser(alias): err}) error = merge_errors(error, err) if validator.discard: try: discarded = set(map(get_field_name, validator.discard)) next_validators = (v for v in validators if not discarded & v.dependencies) validate(obj, next_validators, kwargs, aliaser=aliaser) except ValidationError as err: error = merge_errors(error, err) if error is not None: raise error return obj
def validate_a(self): yield (get_alias(self).a, "b", 0, AliasedStr("c")), f"error {self.a}"
def check_parity_equivalent(self): if (self.parity == Parity.EVEN) != (self.number % 2 == 0): yield get_alias(self).number, "number doesn't respect parity"
def bounds_are_sorted_equivalent(bounded: BoundedValues): min_bound, max_bound = bounded.bounds if min_bound > max_bound: yield get_alias(bounded).bounds, "bounds are not sorted"
def values_dont_exceed_bounds(self): min_bound, max_bound = self.bounds for index, value in enumerate(self.values): if not min_bound <= value <= max_bound: yield (get_alias(self).values, index), "value exceeds bounds"
def bounds_are_sorted(self): min_bound, max_bound = self.bounds if min_bound > max_bound: yield get_alias(self).bounds, "bounds are not sorted"
def check_ips_in_subnet(self): for index, ip in enumerate(self.ips): if ip not in self.subnet: # yield <error path>, <error message> yield (get_alias(self).ips, index), "ip not in subnet"