def validate_sequence(path, data): if not isinstance(data, seq_type): raise er.SequenceTypeInvalid('expected a %s' % seq_type_name, path) # Empty seq schema, allow any data. if not schema: return data out = [] invalid = None errors = [] index_path = UNDEFINED for i, value in enumerate(data): index_path = path + [i] invalid = None for validate in _compiled: try: cval = validate(index_path, value) if cval is not Remove: # do not include Remove values out.append(cval) break except er.Invalid as e: if len(e.path) > len(index_path): raise invalid = e else: errors.append(invalid) if errors: raise er.MultipleInvalid(errors) if _isnamedtuple(data): return type(data)(*out) else: return type(data)(out)
def __call__(self, data): """Validate data against this schema.""" try: return self._compiled([], data) except er.MultipleInvalid: raise except er.Invalid as e: raise er.MultipleInvalid([e])
def validate_dict(path, data): if not isinstance(data, dict): raise er.DictInvalid('expected a dictionary', path) errors = [] for label, group in groups_of_exclusion.items(): exists = False for exclusive in group: if exclusive.schema in data: if exists: msg = exclusive.msg if hasattr(exclusive, 'msg') and exclusive.msg else \ "two or more values in the same group of exclusion '%s'" % label next_path = path + [VirtualPathComponent(label)] errors.append(er.ExclusiveInvalid(msg, next_path)) break exists = True if errors: raise er.MultipleInvalid(errors) for label, group in groups_of_inclusion.items(): included = [node.schema in data for node in group] if any(included) and not all(included): msg = "some but not all values in the same group of inclusion '%s'" % label for g in group: if hasattr(g, 'msg') and g.msg: msg = g.msg break next_path = path + [VirtualPathComponent(label)] errors.append(er.InclusiveInvalid(msg, next_path)) break if errors: raise er.MultipleInvalid(errors) out = data.__class__() return base_validate(path, iteritems(data), out)
def validate_set(path, data): if not isinstance(data, type_): raise er.Invalid('expected a %s' % type_name, path) _compiled = [self._compile(s) for s in schema] errors = [] for value in data: for validate in _compiled: try: validate(path, value) break except er.Invalid: pass else: invalid = er.Invalid('invalid value in %s' % type_name, path) errors.append(invalid) if errors: raise er.MultipleInvalid(errors) return data
def validate_mapping(path, iterable, out): required_keys = all_required_keys.copy() # Build a map of all provided key-value pairs. # The type(out) is used to retain ordering in case a ordered # map type is provided as input. key_value_map = type(out)() for key, value in iterable: key_value_map[key] = value # Insert default values for non-existing keys. for key in all_default_keys: if not isinstance(key.default, Undefined) and \ key.schema not in key_value_map: # A default value has been specified for this missing # key, insert it. key_value_map[key.schema] = key.default() error = None errors = [] for key, value in key_value_map.items(): key_path = path + [key] remove_key = False # Optimization. Validate against the matching key first, then fallback to the rest relevant_candidates = itertools.chain( candidates_by_key.get(key, []), additional_candidates) # compare each given key/value against all compiled key/values # schema key, (compiled key, compiled value) for skey, (ckey, cvalue) in relevant_candidates: try: new_key = ckey(key_path, key) except er.Invalid as e: if len(e.path) > len(key_path): raise if not error or len(e.path) > len(error.path): error = e continue # Backtracking is not performed once a key is selected, so if # the value is invalid we immediately throw an exception. exception_errors = [] # check if the key is marked for removal is_remove = new_key is Remove try: cval = cvalue(key_path, value) # include if it's not marked for removal if not is_remove: out[new_key] = cval else: remove_key = True continue except er.MultipleInvalid as e: exception_errors.extend(e.errors) except er.Invalid as e: exception_errors.append(e) if exception_errors: if is_remove or remove_key: continue for err in exception_errors: if len(err.path) <= len(key_path): err.error_type = invalid_msg errors.append(err) # If there is a validation error for a required # key, this means that the key was provided. # Discard the required key so it does not # create an additional, noisy exception. required_keys.discard(skey) break # Key and value okay, mark as found in case it was # a Required() field. required_keys.discard(skey) break else: if remove_key: # remove key continue elif self.extra == ALLOW_EXTRA: out[key] = value elif self.extra != REMOVE_EXTRA: errors.append( er.Invalid('extra keys not allowed', key_path)) # else REMOVE_EXTRA: ignore the key so it's removed from output # for any required keys left that weren't found and don't have defaults: for key in required_keys: msg = key.msg if hasattr( key, 'msg') and key.msg else 'required key not provided' errors.append(er.RequiredFieldInvalid(msg, path + [key])) if errors: raise er.MultipleInvalid(errors) return out