123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- import re
- from referencing.jsonschema import lookup_recursive_ref
- from jsonschema import _utils
- from jsonschema.exceptions import ValidationError
- def ignore_ref_siblings(schema):
- """
- Ignore siblings of ``$ref`` if it is present.
- Otherwise, return all keywords.
- Suitable for use with `create`'s ``applicable_validators`` argument.
- """
- ref = schema.get("$ref")
- if ref is not None:
- return [("$ref", ref)]
- else:
- return schema.items()
- def dependencies_draft3(validator, dependencies, instance, schema):
- if not validator.is_type(instance, "object"):
- return
- for property, dependency in dependencies.items():
- if property not in instance:
- continue
- if validator.is_type(dependency, "object"):
- yield from validator.descend(
- instance, dependency, schema_path=property,
- )
- elif validator.is_type(dependency, "string"):
- if dependency not in instance:
- message = f"{dependency!r} is a dependency of {property!r}"
- yield ValidationError(message)
- else:
- for each in dependency:
- if each not in instance:
- message = f"{each!r} is a dependency of {property!r}"
- yield ValidationError(message)
- def dependencies_draft4_draft6_draft7(
- validator,
- dependencies,
- instance,
- schema,
- ):
- """
- Support for the ``dependencies`` keyword from pre-draft 2019-09.
- In later drafts, the keyword was split into separate
- ``dependentRequired`` and ``dependentSchemas`` validators.
- """
- if not validator.is_type(instance, "object"):
- return
- for property, dependency in dependencies.items():
- if property not in instance:
- continue
- if validator.is_type(dependency, "array"):
- for each in dependency:
- if each not in instance:
- message = f"{each!r} is a dependency of {property!r}"
- yield ValidationError(message)
- else:
- yield from validator.descend(
- instance, dependency, schema_path=property,
- )
- def disallow_draft3(validator, disallow, instance, schema):
- for disallowed in _utils.ensure_list(disallow):
- if validator.evolve(schema={"type": [disallowed]}).is_valid(instance):
- message = f"{disallowed!r} is disallowed for {instance!r}"
- yield ValidationError(message)
- def extends_draft3(validator, extends, instance, schema):
- if validator.is_type(extends, "object"):
- yield from validator.descend(instance, extends)
- return
- for index, subschema in enumerate(extends):
- yield from validator.descend(instance, subschema, schema_path=index)
- def items_draft3_draft4(validator, items, instance, schema):
- if not validator.is_type(instance, "array"):
- return
- if validator.is_type(items, "object"):
- for index, item in enumerate(instance):
- yield from validator.descend(item, items, path=index)
- else:
- for (index, item), subschema in zip(enumerate(instance), items):
- yield from validator.descend(
- item, subschema, path=index, schema_path=index,
- )
- def additionalItems(validator, aI, instance, schema):
- if (
- not validator.is_type(instance, "array")
- or validator.is_type(schema.get("items", {}), "object")
- ):
- return
- len_items = len(schema.get("items", []))
- if validator.is_type(aI, "object"):
- for index, item in enumerate(instance[len_items:], start=len_items):
- yield from validator.descend(item, aI, path=index)
- elif not aI and len(instance) > len(schema.get("items", [])):
- error = "Additional items are not allowed (%s %s unexpected)"
- yield ValidationError(
- error % _utils.extras_msg(instance[len(schema.get("items", [])):]),
- )
- def items_draft6_draft7_draft201909(validator, items, instance, schema):
- if not validator.is_type(instance, "array"):
- return
- if validator.is_type(items, "array"):
- for (index, item), subschema in zip(enumerate(instance), items):
- yield from validator.descend(
- item, subschema, path=index, schema_path=index,
- )
- else:
- for index, item in enumerate(instance):
- yield from validator.descend(item, items, path=index)
- def minimum_draft3_draft4(validator, minimum, instance, schema):
- if not validator.is_type(instance, "number"):
- return
- if schema.get("exclusiveMinimum", False):
- failed = instance <= minimum
- cmp = "less than or equal to"
- else:
- failed = instance < minimum
- cmp = "less than"
- if failed:
- message = f"{instance!r} is {cmp} the minimum of {minimum!r}"
- yield ValidationError(message)
- def maximum_draft3_draft4(validator, maximum, instance, schema):
- if not validator.is_type(instance, "number"):
- return
- if schema.get("exclusiveMaximum", False):
- failed = instance >= maximum
- cmp = "greater than or equal to"
- else:
- failed = instance > maximum
- cmp = "greater than"
- if failed:
- message = f"{instance!r} is {cmp} the maximum of {maximum!r}"
- yield ValidationError(message)
- def properties_draft3(validator, properties, instance, schema):
- if not validator.is_type(instance, "object"):
- return
- for property, subschema in properties.items():
- if property in instance:
- yield from validator.descend(
- instance[property],
- subschema,
- path=property,
- schema_path=property,
- )
- elif subschema.get("required", False):
- error = ValidationError(f"{property!r} is a required property")
- error._set(
- validator="required",
- validator_value=subschema["required"],
- instance=instance,
- schema=schema,
- )
- error.path.appendleft(property)
- error.schema_path.extend([property, "required"])
- yield error
- def type_draft3(validator, types, instance, schema):
- types = _utils.ensure_list(types)
- all_errors = []
- for index, type in enumerate(types):
- if validator.is_type(type, "object"):
- errors = list(validator.descend(instance, type, schema_path=index))
- if not errors:
- return
- all_errors.extend(errors)
- elif validator.is_type(instance, type):
- return
- reprs = []
- for type in types:
- try:
- reprs.append(repr(type["name"]))
- except Exception: # noqa: BLE001
- reprs.append(repr(type))
- yield ValidationError(
- f"{instance!r} is not of type {', '.join(reprs)}",
- context=all_errors,
- )
- def contains_draft6_draft7(validator, contains, instance, schema):
- if not validator.is_type(instance, "array"):
- return
- if not any(
- validator.evolve(schema=contains).is_valid(element)
- for element in instance
- ):
- yield ValidationError(
- f"None of {instance!r} are valid under the given schema",
- )
- def recursiveRef(validator, recursiveRef, instance, schema):
- resolved = lookup_recursive_ref(validator._resolver)
- yield from validator.descend(
- instance,
- resolved.contents,
- resolver=resolved.resolver,
- )
- def find_evaluated_item_indexes_by_schema(validator, instance, schema):
- """
- Get all indexes of items that get evaluated under the current schema.
- Covers all keywords related to unevaluatedItems: items, prefixItems, if,
- then, else, contains, unevaluatedItems, allOf, oneOf, anyOf
- """
- if validator.is_type(schema, "boolean"):
- return []
- evaluated_indexes = []
- ref = schema.get("$ref")
- if ref is not None:
- resolved = validator._resolver.lookup(ref)
- evaluated_indexes.extend(
- find_evaluated_item_indexes_by_schema(
- validator.evolve(
- schema=resolved.contents,
- _resolver=resolved.resolver,
- ),
- instance,
- resolved.contents,
- ),
- )
- if "$recursiveRef" in schema:
- resolved = lookup_recursive_ref(validator._resolver)
- evaluated_indexes.extend(
- find_evaluated_item_indexes_by_schema(
- validator.evolve(
- schema=resolved.contents,
- _resolver=resolved.resolver,
- ),
- instance,
- resolved.contents,
- ),
- )
- if "items" in schema:
- if "additionalItems" in schema:
- return list(range(len(instance)))
- if validator.is_type(schema["items"], "object"):
- return list(range(len(instance)))
- evaluated_indexes += list(range(len(schema["items"])))
- if "if" in schema:
- if validator.evolve(schema=schema["if"]).is_valid(instance):
- evaluated_indexes += find_evaluated_item_indexes_by_schema(
- validator, instance, schema["if"],
- )
- if "then" in schema:
- evaluated_indexes += find_evaluated_item_indexes_by_schema(
- validator, instance, schema["then"],
- )
- elif "else" in schema:
- evaluated_indexes += find_evaluated_item_indexes_by_schema(
- validator, instance, schema["else"],
- )
- for keyword in ["contains", "unevaluatedItems"]:
- if keyword in schema:
- for k, v in enumerate(instance):
- if validator.evolve(schema=schema[keyword]).is_valid(v):
- evaluated_indexes.append(k)
- for keyword in ["allOf", "oneOf", "anyOf"]:
- if keyword in schema:
- for subschema in schema[keyword]:
- errs = next(validator.descend(instance, subschema), None)
- if errs is None:
- evaluated_indexes += find_evaluated_item_indexes_by_schema(
- validator, instance, subschema,
- )
- return evaluated_indexes
- def unevaluatedItems_draft2019(validator, unevaluatedItems, instance, schema):
- if not validator.is_type(instance, "array"):
- return
- evaluated_item_indexes = find_evaluated_item_indexes_by_schema(
- validator, instance, schema,
- )
- unevaluated_items = [
- item for index, item in enumerate(instance)
- if index not in evaluated_item_indexes
- ]
- if unevaluated_items:
- error = "Unevaluated items are not allowed (%s %s unexpected)"
- yield ValidationError(error % _utils.extras_msg(unevaluated_items))
- def find_evaluated_property_keys_by_schema(validator, instance, schema):
- if validator.is_type(schema, "boolean"):
- return []
- evaluated_keys = []
- ref = schema.get("$ref")
- if ref is not None:
- resolved = validator._resolver.lookup(ref)
- evaluated_keys.extend(
- find_evaluated_property_keys_by_schema(
- validator.evolve(
- schema=resolved.contents,
- _resolver=resolved.resolver,
- ),
- instance,
- resolved.contents,
- ),
- )
- if "$recursiveRef" in schema:
- resolved = lookup_recursive_ref(validator._resolver)
- evaluated_keys.extend(
- find_evaluated_property_keys_by_schema(
- validator.evolve(
- schema=resolved.contents,
- _resolver=resolved.resolver,
- ),
- instance,
- resolved.contents,
- ),
- )
- for keyword in [
- "properties", "additionalProperties", "unevaluatedProperties",
- ]:
- if keyword in schema:
- schema_value = schema[keyword]
- if validator.is_type(schema_value, "boolean") and schema_value:
- evaluated_keys += instance.keys()
- elif validator.is_type(schema_value, "object"):
- for property in schema_value:
- if property in instance:
- evaluated_keys.append(property)
- if "patternProperties" in schema:
- for property in instance:
- for pattern in schema["patternProperties"]:
- if re.search(pattern, property):
- evaluated_keys.append(property)
- if "dependentSchemas" in schema:
- for property, subschema in schema["dependentSchemas"].items():
- if property not in instance:
- continue
- evaluated_keys += find_evaluated_property_keys_by_schema(
- validator, instance, subschema,
- )
- for keyword in ["allOf", "oneOf", "anyOf"]:
- if keyword in schema:
- for subschema in schema[keyword]:
- errs = next(validator.descend(instance, subschema), None)
- if errs is None:
- evaluated_keys += find_evaluated_property_keys_by_schema(
- validator, instance, subschema,
- )
- if "if" in schema:
- if validator.evolve(schema=schema["if"]).is_valid(instance):
- evaluated_keys += find_evaluated_property_keys_by_schema(
- validator, instance, schema["if"],
- )
- if "then" in schema:
- evaluated_keys += find_evaluated_property_keys_by_schema(
- validator, instance, schema["then"],
- )
- elif "else" in schema:
- evaluated_keys += find_evaluated_property_keys_by_schema(
- validator, instance, schema["else"],
- )
- return evaluated_keys
- def unevaluatedProperties_draft2019(validator, uP, instance, schema):
- if not validator.is_type(instance, "object"):
- return
- evaluated_keys = find_evaluated_property_keys_by_schema(
- validator, instance, schema,
- )
- unevaluated_keys = []
- for property in instance:
- if property not in evaluated_keys:
- for _ in validator.descend(
- instance[property],
- uP,
- path=property,
- schema_path=property,
- ):
- # FIXME: Include context for each unevaluated property
- # indicating why it's invalid under the subschema.
- unevaluated_keys.append(property) # noqa: PERF401
- if unevaluated_keys:
- if uP is False:
- error = "Unevaluated properties are not allowed (%s %s unexpected)"
- extras = sorted(unevaluated_keys, key=str)
- yield ValidationError(error % _utils.extras_msg(extras))
- else:
- error = (
- "Unevaluated properties are not valid under "
- "the given schema (%s %s unevaluated and invalid)"
- )
- yield ValidationError(error % _utils.extras_msg(unevaluated_keys))
|