You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			450 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			450 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
from fractions import Fraction
 | 
						|
import re
 | 
						|
 | 
						|
from jsonschema._utils import (
 | 
						|
    ensure_list,
 | 
						|
    equal,
 | 
						|
    extras_msg,
 | 
						|
    find_additional_properties,
 | 
						|
    find_evaluated_item_indexes_by_schema,
 | 
						|
    find_evaluated_property_keys_by_schema,
 | 
						|
    uniq,
 | 
						|
)
 | 
						|
from jsonschema.exceptions import FormatError, ValidationError
 | 
						|
 | 
						|
 | 
						|
def patternProperties(validator, patternProperties, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
 | 
						|
    for pattern, subschema in patternProperties.items():
 | 
						|
        for k, v in instance.items():
 | 
						|
            if re.search(pattern, k):
 | 
						|
                yield from validator.descend(
 | 
						|
                    v, subschema, path=k, schema_path=pattern,
 | 
						|
                )
 | 
						|
 | 
						|
 | 
						|
def propertyNames(validator, propertyNames, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
 | 
						|
    for property in instance:
 | 
						|
        yield from validator.descend(instance=property, schema=propertyNames)
 | 
						|
 | 
						|
 | 
						|
def additionalProperties(validator, aP, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
 | 
						|
    extras = set(find_additional_properties(instance, schema))
 | 
						|
 | 
						|
    if validator.is_type(aP, "object"):
 | 
						|
        for extra in extras:
 | 
						|
            yield from validator.descend(instance[extra], aP, path=extra)
 | 
						|
    elif not aP and extras:
 | 
						|
        if "patternProperties" in schema:
 | 
						|
            verb = "does" if len(extras) == 1 else "do"
 | 
						|
            joined = ", ".join(repr(each) for each in sorted(extras))
 | 
						|
            patterns = ", ".join(
 | 
						|
                repr(each) for each in sorted(schema["patternProperties"])
 | 
						|
            )
 | 
						|
            error = f"{joined} {verb} not match any of the regexes: {patterns}"
 | 
						|
            yield ValidationError(error)
 | 
						|
        else:
 | 
						|
            error = "Additional properties are not allowed (%s %s unexpected)"
 | 
						|
            yield ValidationError(error % extras_msg(sorted(extras, key=str)))
 | 
						|
 | 
						|
 | 
						|
def items(validator, items, instance, schema):
 | 
						|
    if not validator.is_type(instance, "array"):
 | 
						|
        return
 | 
						|
 | 
						|
    prefix = len(schema.get("prefixItems", []))
 | 
						|
    total = len(instance)
 | 
						|
    extra = total - prefix
 | 
						|
    if extra <= 0:
 | 
						|
        return
 | 
						|
 | 
						|
    if items is False:
 | 
						|
        rest = instance[prefix:] if extra != 1 else instance[prefix]
 | 
						|
        item = "items" if prefix != 1 else "item"
 | 
						|
        yield ValidationError(
 | 
						|
            f"Expected at most {prefix} {item} but found {extra} "
 | 
						|
            f"extra: {rest!r}",
 | 
						|
        )
 | 
						|
    else:
 | 
						|
        for index in range(prefix, total):
 | 
						|
            yield from validator.descend(
 | 
						|
                instance=instance[index],
 | 
						|
                schema=items,
 | 
						|
                path=index,
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
def const(validator, const, instance, schema):
 | 
						|
    if not equal(instance, const):
 | 
						|
        yield ValidationError(f"{const!r} was expected")
 | 
						|
 | 
						|
 | 
						|
def contains(validator, contains, instance, schema):
 | 
						|
    if not validator.is_type(instance, "array"):
 | 
						|
        return
 | 
						|
 | 
						|
    matches = 0
 | 
						|
    min_contains = schema.get("minContains", 1)
 | 
						|
    max_contains = schema.get("maxContains", len(instance))
 | 
						|
 | 
						|
    contains_validator = validator.evolve(schema=contains)
 | 
						|
 | 
						|
    for each in instance:
 | 
						|
        if contains_validator.is_valid(each):
 | 
						|
            matches += 1
 | 
						|
            if matches > max_contains:
 | 
						|
                yield ValidationError(
 | 
						|
                    "Too many items match the given schema "
 | 
						|
                    f"(expected at most {max_contains})",
 | 
						|
                    validator="maxContains",
 | 
						|
                    validator_value=max_contains,
 | 
						|
                )
 | 
						|
                return
 | 
						|
 | 
						|
    if matches < min_contains:
 | 
						|
        if not matches:
 | 
						|
            yield ValidationError(
 | 
						|
                f"{instance!r} does not contain items "
 | 
						|
                "matching the given schema",
 | 
						|
            )
 | 
						|
        else:
 | 
						|
            yield ValidationError(
 | 
						|
                "Too few items match the given schema (expected at least "
 | 
						|
                f"{min_contains} but only {matches} matched)",
 | 
						|
                validator="minContains",
 | 
						|
                validator_value=min_contains,
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
def exclusiveMinimum(validator, minimum, instance, schema):
 | 
						|
    if not validator.is_type(instance, "number"):
 | 
						|
        return
 | 
						|
 | 
						|
    if instance <= minimum:
 | 
						|
        yield ValidationError(
 | 
						|
            f"{instance!r} is less than or equal to "
 | 
						|
            f"the minimum of {minimum!r}",
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
def exclusiveMaximum(validator, maximum, instance, schema):
 | 
						|
    if not validator.is_type(instance, "number"):
 | 
						|
        return
 | 
						|
 | 
						|
    if instance >= maximum:
 | 
						|
        yield ValidationError(
 | 
						|
            f"{instance!r} is greater than or equal "
 | 
						|
            f"to the maximum of {maximum!r}",
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
def minimum(validator, minimum, instance, schema):
 | 
						|
    if not validator.is_type(instance, "number"):
 | 
						|
        return
 | 
						|
 | 
						|
    if instance < minimum:
 | 
						|
        message = f"{instance!r} is less than the minimum of {minimum!r}"
 | 
						|
        yield ValidationError(message)
 | 
						|
 | 
						|
 | 
						|
def maximum(validator, maximum, instance, schema):
 | 
						|
    if not validator.is_type(instance, "number"):
 | 
						|
        return
 | 
						|
 | 
						|
    if instance > maximum:
 | 
						|
        message = f"{instance!r} is greater than the maximum of {maximum!r}"
 | 
						|
        yield ValidationError(message)
 | 
						|
 | 
						|
 | 
						|
def multipleOf(validator, dB, instance, schema):
 | 
						|
    if not validator.is_type(instance, "number"):
 | 
						|
        return
 | 
						|
 | 
						|
    if isinstance(dB, float):
 | 
						|
        quotient = instance / dB
 | 
						|
        try:
 | 
						|
            failed = int(quotient) != quotient
 | 
						|
        except OverflowError:
 | 
						|
            # When `instance` is large and `dB` is less than one,
 | 
						|
            # quotient can overflow to infinity; and then casting to int
 | 
						|
            # raises an error.
 | 
						|
            #
 | 
						|
            # In this case we fall back to Fraction logic, which is
 | 
						|
            # exact and cannot overflow.  The performance is also
 | 
						|
            # acceptable: we try the fast all-float option first, and
 | 
						|
            # we know that fraction(dB) can have at most a few hundred
 | 
						|
            # digits in each part.  The worst-case slowdown is therefore
 | 
						|
            # for already-slow enormous integers or Decimals.
 | 
						|
            failed = (Fraction(instance) / Fraction(dB)).denominator != 1
 | 
						|
    else:
 | 
						|
        failed = instance % dB
 | 
						|
 | 
						|
    if failed:
 | 
						|
        yield ValidationError(f"{instance!r} is not a multiple of {dB}")
 | 
						|
 | 
						|
 | 
						|
def minItems(validator, mI, instance, schema):
 | 
						|
    if validator.is_type(instance, "array") and len(instance) < mI:
 | 
						|
        message = "should be non-empty" if mI == 1 else "is too short"
 | 
						|
        yield ValidationError(f"{instance!r} {message}")
 | 
						|
 | 
						|
 | 
						|
def maxItems(validator, mI, instance, schema):
 | 
						|
    if validator.is_type(instance, "array") and len(instance) > mI:
 | 
						|
        message = "is expected to be empty" if mI == 0 else "is too long"
 | 
						|
        yield ValidationError(f"{instance!r} {message}")
 | 
						|
 | 
						|
 | 
						|
def uniqueItems(validator, uI, instance, schema):
 | 
						|
    if (
 | 
						|
        uI
 | 
						|
        and validator.is_type(instance, "array")
 | 
						|
        and not uniq(instance)
 | 
						|
    ):
 | 
						|
        yield ValidationError(f"{instance!r} has non-unique elements")
 | 
						|
 | 
						|
 | 
						|
def pattern(validator, patrn, instance, schema):
 | 
						|
    if (
 | 
						|
        validator.is_type(instance, "string")
 | 
						|
        and not re.search(patrn, instance)
 | 
						|
    ):
 | 
						|
        yield ValidationError(f"{instance!r} does not match {patrn!r}")
 | 
						|
 | 
						|
 | 
						|
def format(validator, format, instance, schema):
 | 
						|
    if validator.format_checker is not None:
 | 
						|
        try:
 | 
						|
            validator.format_checker.check(instance, format)
 | 
						|
        except FormatError as error:
 | 
						|
            yield ValidationError(error.message, cause=error.cause)
 | 
						|
 | 
						|
 | 
						|
def minLength(validator, mL, instance, schema):
 | 
						|
    if validator.is_type(instance, "string") and len(instance) < mL:
 | 
						|
        message = "should be non-empty" if mL == 1 else "is too short"
 | 
						|
        yield ValidationError(f"{instance!r} {message}")
 | 
						|
 | 
						|
 | 
						|
def maxLength(validator, mL, instance, schema):
 | 
						|
    if validator.is_type(instance, "string") and len(instance) > mL:
 | 
						|
        message = "is expected to be empty" if mL == 0 else "is too long"
 | 
						|
        yield ValidationError(f"{instance!r} {message}")
 | 
						|
 | 
						|
 | 
						|
def dependentRequired(validator, dependentRequired, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
 | 
						|
    for property, dependency in dependentRequired.items():
 | 
						|
        if property not in instance:
 | 
						|
            continue
 | 
						|
 | 
						|
        for each in dependency:
 | 
						|
            if each not in instance:
 | 
						|
                message = f"{each!r} is a dependency of {property!r}"
 | 
						|
                yield ValidationError(message)
 | 
						|
 | 
						|
 | 
						|
def dependentSchemas(validator, dependentSchemas, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
 | 
						|
    for property, dependency in dependentSchemas.items():
 | 
						|
        if property not in instance:
 | 
						|
            continue
 | 
						|
        yield from validator.descend(
 | 
						|
            instance, dependency, schema_path=property,
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
def enum(validator, enums, instance, schema):
 | 
						|
    if all(not equal(each, instance) for each in enums):
 | 
						|
        yield ValidationError(f"{instance!r} is not one of {enums!r}")
 | 
						|
 | 
						|
 | 
						|
def ref(validator, ref, instance, schema):
 | 
						|
    yield from validator._validate_reference(ref=ref, instance=instance)
 | 
						|
 | 
						|
 | 
						|
def dynamicRef(validator, dynamicRef, instance, schema):
 | 
						|
    yield from validator._validate_reference(ref=dynamicRef, instance=instance)
 | 
						|
 | 
						|
 | 
						|
def type(validator, types, instance, schema):
 | 
						|
    types = ensure_list(types)
 | 
						|
 | 
						|
    if not any(validator.is_type(instance, type) for type in types):
 | 
						|
        reprs = ", ".join(repr(type) for type in types)
 | 
						|
        yield ValidationError(f"{instance!r} is not of type {reprs}")
 | 
						|
 | 
						|
 | 
						|
def properties(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,
 | 
						|
            )
 | 
						|
 | 
						|
 | 
						|
def required(validator, required, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
    for property in required:
 | 
						|
        if property not in instance:
 | 
						|
            yield ValidationError(f"{property!r} is a required property")
 | 
						|
 | 
						|
 | 
						|
def minProperties(validator, mP, instance, schema):
 | 
						|
    if validator.is_type(instance, "object") and len(instance) < mP:
 | 
						|
        message = (
 | 
						|
            "should be non-empty" if mP == 1
 | 
						|
            else "does not have enough properties"
 | 
						|
        )
 | 
						|
        yield ValidationError(f"{instance!r} {message}")
 | 
						|
 | 
						|
 | 
						|
def maxProperties(validator, mP, instance, schema):
 | 
						|
    if not validator.is_type(instance, "object"):
 | 
						|
        return
 | 
						|
    if validator.is_type(instance, "object") and len(instance) > mP:
 | 
						|
        message = (
 | 
						|
            "is expected to be empty" if mP == 0
 | 
						|
            else "has too many properties"
 | 
						|
        )
 | 
						|
        yield ValidationError(f"{instance!r} {message}")
 | 
						|
 | 
						|
 | 
						|
def allOf(validator, allOf, instance, schema):
 | 
						|
    for index, subschema in enumerate(allOf):
 | 
						|
        yield from validator.descend(instance, subschema, schema_path=index)
 | 
						|
 | 
						|
 | 
						|
def anyOf(validator, anyOf, instance, schema):
 | 
						|
    all_errors = []
 | 
						|
    for index, subschema in enumerate(anyOf):
 | 
						|
        errs = list(validator.descend(instance, subschema, schema_path=index))
 | 
						|
        if not errs:
 | 
						|
            break
 | 
						|
        all_errors.extend(errs)
 | 
						|
    else:
 | 
						|
        yield ValidationError(
 | 
						|
            f"{instance!r} is not valid under any of the given schemas",
 | 
						|
            context=all_errors,
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
def oneOf(validator, oneOf, instance, schema):
 | 
						|
    subschemas = enumerate(oneOf)
 | 
						|
    all_errors = []
 | 
						|
    for index, subschema in subschemas:
 | 
						|
        errs = list(validator.descend(instance, subschema, schema_path=index))
 | 
						|
        if not errs:
 | 
						|
            first_valid = subschema
 | 
						|
            break
 | 
						|
        all_errors.extend(errs)
 | 
						|
    else:
 | 
						|
        yield ValidationError(
 | 
						|
            f"{instance!r} is not valid under any of the given schemas",
 | 
						|
            context=all_errors,
 | 
						|
        )
 | 
						|
 | 
						|
    more_valid = [
 | 
						|
        each for _, each in subschemas
 | 
						|
        if validator.evolve(schema=each).is_valid(instance)
 | 
						|
    ]
 | 
						|
    if more_valid:
 | 
						|
        more_valid.append(first_valid)
 | 
						|
        reprs = ", ".join(repr(schema) for schema in more_valid)
 | 
						|
        yield ValidationError(f"{instance!r} is valid under each of {reprs}")
 | 
						|
 | 
						|
 | 
						|
def not_(validator, not_schema, instance, schema):
 | 
						|
    if validator.evolve(schema=not_schema).is_valid(instance):
 | 
						|
        message = f"{instance!r} should not be valid under {not_schema!r}"
 | 
						|
        yield ValidationError(message)
 | 
						|
 | 
						|
 | 
						|
def if_(validator, if_schema, instance, schema):
 | 
						|
    if validator.evolve(schema=if_schema).is_valid(instance):
 | 
						|
        if "then" in schema:
 | 
						|
            then = schema["then"]
 | 
						|
            yield from validator.descend(instance, then, schema_path="then")
 | 
						|
    elif "else" in schema:
 | 
						|
        else_ = schema["else"]
 | 
						|
        yield from validator.descend(instance, else_, schema_path="else")
 | 
						|
 | 
						|
 | 
						|
def unevaluatedItems(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 % extras_msg(unevaluated_items))
 | 
						|
 | 
						|
 | 
						|
def unevaluatedProperties(validator, unevaluatedProperties, 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],
 | 
						|
                unevaluatedProperties,
 | 
						|
                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 unevaluatedProperties is False:
 | 
						|
            error = "Unevaluated properties are not allowed (%s %s unexpected)"
 | 
						|
            extras = sorted(unevaluated_keys, key=str)
 | 
						|
            yield ValidationError(error % extras_msg(extras))
 | 
						|
        else:
 | 
						|
            error = (
 | 
						|
                "Unevaluated properties are not valid under "
 | 
						|
                "the given schema (%s %s unevaluated and invalid)"
 | 
						|
            )
 | 
						|
            yield ValidationError(error % extras_msg(unevaluated_keys))
 | 
						|
 | 
						|
 | 
						|
def prefixItems(validator, prefixItems, instance, schema):
 | 
						|
    if not validator.is_type(instance, "array"):
 | 
						|
        return
 | 
						|
 | 
						|
    for (index, item), subschema in zip(enumerate(instance), prefixItems):
 | 
						|
        yield from validator.descend(
 | 
						|
            instance=item,
 | 
						|
            schema=subschema,
 | 
						|
            schema_path=index,
 | 
						|
            path=index,
 | 
						|
        )
 |