about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/jsonschema/_keywords.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/jsonschema/_keywords.py')
-rw-r--r--.venv/lib/python3.12/site-packages/jsonschema/_keywords.py449
1 files changed, 449 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/jsonschema/_keywords.py b/.venv/lib/python3.12/site-packages/jsonschema/_keywords.py
new file mode 100644
index 00000000..f30f9541
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/jsonschema/_keywords.py
@@ -0,0 +1,449 @@
+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,
+        )