diff --git a/jsonschema/_evaluated.py b/jsonschema/_evaluated.py new file mode 100644 index 000000000..e078ebb7d --- /dev/null +++ b/jsonschema/_evaluated.py @@ -0,0 +1,185 @@ +""" +Evaluated item and property index tracking for unevaluated* keywords. + +These functions determine which array items and object properties have been +evaluated by a schema, which is needed to implement the unevaluatedItems +and unevaluatedProperties keywords from JSON Schema 2020-12. + +Previously lived in _utils.py — extracted here for single responsibility: +one module = one concern (evaluated item/property tracking). +""" +import re + + +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 = [] + + if "items" in schema: + return list(range(len(instance))) + + 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, + ), + ) + + dynamicRef = schema.get("$dynamicRef") + if dynamicRef is not None: + resolved = validator._resolver.lookup(dynamicRef) + evaluated_indexes.extend( + find_evaluated_item_indexes_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + if "prefixItems" in schema: + evaluated_indexes += list(range(len(schema["prefixItems"]))) + + 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 find_evaluated_property_keys_by_schema(validator, instance, schema): + """ + Get all keys of items that get evaluated under the current schema. + + Covers all keywords related to unevaluatedProperties: properties, + additionalProperties, unevaluatedProperties, patternProperties, + dependentSchemas, allOf, oneOf, anyOf, if, then, else + """ + 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, + ), + ) + + dynamicRef = schema.get("$dynamicRef") + if dynamicRef is not None: + resolved = validator._resolver.lookup(dynamicRef) + evaluated_keys.extend( + find_evaluated_property_keys_by_schema( + validator.evolve( + schema=resolved.contents, + _resolver=resolved.resolver, + ), + instance, + resolved.contents, + ), + ) + + properties = schema.get("properties") + if validator.is_type(properties, "object"): + evaluated_keys += properties.keys() & instance.keys() + + for keyword in ["additionalProperties", "unevaluatedProperties"]: + if (subschema := schema.get(keyword)) is None: + continue + evaluated_keys += ( + key + for key, value in instance.items() + if is_valid(validator.descend(value, subschema)) + ) + + 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"]: + for subschema in schema.get(keyword, []): + if not is_valid(validator.descend(instance, subschema)): + continue + 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 is_valid(errs_it): + """Whether there are no errors in the given iterator.""" + return next(errs_it, None) is None diff --git a/jsonschema/_keywords_draft3.py b/jsonschema/_keywords_draft3.py new file mode 100644 index 000000000..a251571bc --- /dev/null +++ b/jsonschema/_keywords_draft3.py @@ -0,0 +1,449 @@ +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, + ), + ) + + properties = schema.get("properties") + if validator.is_type(properties, "object"): + evaluated_keys += properties.keys() & instance.keys() + + for keyword in ["additionalProperties", "unevaluatedProperties"]: + if (subschema := schema.get(keyword)) is None: + continue + evaluated_keys += ( + key + for key, value in instance.items() + if _utils.is_valid(validator.descend(value, subschema)) + ) + + 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)) diff --git a/jsonschema/_uri.py b/jsonschema/_uri.py new file mode 100644 index 000000000..c39b2ecc1 --- /dev/null +++ b/jsonschema/_uri.py @@ -0,0 +1,39 @@ +""" +URI-related utility classes. + +Extracted from _utils.py for single responsibility: +one module = one concern (URI normalization and lookup). +""" +from collections.abc import MutableMapping +from urllib.parse import urlsplit + + +class URIDict(MutableMapping): + """ + Dictionary which uses normalized URIs as keys. + """ + + def normalize(self, uri): + return urlsplit(uri).geturl() + + def __init__(self, *args, **kwargs): + self.store = dict() + self.store.update(*args, **kwargs) + + def __getitem__(self, uri): + return self.store[self.normalize(uri)] + + def __setitem__(self, uri, value): + self.store[self.normalize(uri)] = value + + def __delitem__(self, uri): + del self.store[self.normalize(uri)] + + def __iter__(self): + return iter(self.store) + + def __len__(self): # pragma: no cover -- untested, but to be removed + return len(self.store) + + def __repr__(self): # pragma: no cover -- untested, but to be removed + return repr(self.store) diff --git a/jsonschema/_utils.py b/jsonschema/_utils.py index 3177caea3..d341330ba 100644 --- a/jsonschema/_utils.py +++ b/jsonschema/_utils.py @@ -1,7 +1,19 @@ -from collections.abc import Mapping, MutableMapping, Sequence -from urllib.parse import urlsplit +""" +General-purpose utilities for JSON Schema validation. + +This module contains equality comparison, uniqueness checking, and +schema introspection utilities used across the jsonschema package. + +Moved to dedicated modules for single responsibility: +- URIDict → _uri.py (URI normalization concern) +- find_evaluated_* → _evaluated.py (evaluated item/property tracking) +- is_valid → _evaluated.py (closely related to evaluated tracking) +""" +from collections.abc import Mapping, Sequence import re +# Re-export from new modules for backward compatibility + # Module-level sentinels so recursive `_uniq_key` calls produce comparable # keys for nested True/False (function-default sentinels would also work, but # this makes the intent explicit). @@ -10,37 +22,6 @@ _UNSUPPORTED = object() -class URIDict(MutableMapping): - """ - Dictionary which uses normalized URIs as keys. - """ - - def normalize(self, uri): - return urlsplit(uri).geturl() - - def __init__(self, *args, **kwargs): - self.store = dict() - self.store.update(*args, **kwargs) - - def __getitem__(self, uri): - return self.store[self.normalize(uri)] - - def __setitem__(self, uri, value): - self.store[self.normalize(uri)] = value - - def __delitem__(self, uri): - del self.store[self.normalize(uri)] - - def __iter__(self): - return iter(self.store) - - def __len__(self): # pragma: no cover -- untested, but to be removed - return len(self.store) - - def __repr__(self): # pragma: no cover -- untested, but to be removed - return repr(self.store) - - class Unset: """ An as-of-yet unset attribute or unprovided default parameter. @@ -235,177 +216,3 @@ def uniq(container): seen_keys.add(key) return True - - -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 = [] - - if "items" in schema: - return list(range(len(instance))) - - 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, - ), - ) - - dynamicRef = schema.get("$dynamicRef") - if dynamicRef is not None: - resolved = validator._resolver.lookup(dynamicRef) - evaluated_indexes.extend( - find_evaluated_item_indexes_by_schema( - validator.evolve( - schema=resolved.contents, - _resolver=resolved.resolver, - ), - instance, - resolved.contents, - ), - ) - - if "prefixItems" in schema: - evaluated_indexes += list(range(len(schema["prefixItems"]))) - - 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 find_evaluated_property_keys_by_schema(validator, instance, schema): - """ - Get all keys of items that get evaluated under the current schema. - - Covers all keywords related to unevaluatedProperties: properties, - additionalProperties, unevaluatedProperties, patternProperties, - dependentSchemas, allOf, oneOf, anyOf, if, then, else - """ - 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, - ), - ) - - dynamicRef = schema.get("$dynamicRef") - if dynamicRef is not None: - resolved = validator._resolver.lookup(dynamicRef) - evaluated_keys.extend( - find_evaluated_property_keys_by_schema( - validator.evolve( - schema=resolved.contents, - _resolver=resolved.resolver, - ), - instance, - resolved.contents, - ), - ) - - properties = schema.get("properties") - if validator.is_type(properties, "object"): - evaluated_keys += properties.keys() & instance.keys() - - for keyword in ["additionalProperties", "unevaluatedProperties"]: - if (subschema := schema.get(keyword)) is None: - continue - evaluated_keys += ( - key - for key, value in instance.items() - if is_valid(validator.descend(value, subschema)) - ) - - 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"]: - for subschema in schema.get(keyword, []): - if not is_valid(validator.descend(instance, subschema)): - continue - 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 is_valid(errs_it): - """Whether there are no errors in the given iterator.""" - return next(errs_it, None) is None diff --git a/jsonschema/validators.py b/jsonschema/validators.py index 98ddf6bfb..74c48c708 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -24,7 +24,7 @@ from jsonschema import ( _format, _keywords, - _legacy_keywords, + _keywords_draft3, _types, _typing, _utils, @@ -604,32 +604,32 @@ def extend( ), validators={ "$ref": _keywords.ref, - "additionalItems": _legacy_keywords.additionalItems, + "additionalItems": _keywords_draft3.additionalItems, "additionalProperties": _keywords.additionalProperties, - "dependencies": _legacy_keywords.dependencies_draft3, - "disallow": _legacy_keywords.disallow_draft3, + "dependencies": _keywords_draft3.dependencies_draft3, + "disallow": _keywords_draft3.disallow_draft3, "divisibleBy": _keywords.multipleOf, "enum": _keywords.enum, - "extends": _legacy_keywords.extends_draft3, + "extends": _keywords_draft3.extends_draft3, "format": _keywords.format, - "items": _legacy_keywords.items_draft3_draft4, + "items": _keywords_draft3.items_draft3_draft4, "maxItems": _keywords.maxItems, "maxLength": _keywords.maxLength, - "maximum": _legacy_keywords.maximum_draft3_draft4, + "maximum": _keywords_draft3.maximum_draft3_draft4, "minItems": _keywords.minItems, "minLength": _keywords.minLength, - "minimum": _legacy_keywords.minimum_draft3_draft4, + "minimum": _keywords_draft3.minimum_draft3_draft4, "pattern": _keywords.pattern, "patternProperties": _keywords.patternProperties, - "properties": _legacy_keywords.properties_draft3, - "type": _legacy_keywords.type_draft3, + "properties": _keywords_draft3.properties_draft3, + "type": _keywords_draft3.type_draft3, "uniqueItems": _keywords.uniqueItems, }, type_checker=_types.draft3_type_checker, format_checker=_format.draft3_format_checker, version="draft3", id_of=referencing.jsonschema.DRAFT3.id_of, - applicable_validators=_legacy_keywords.ignore_ref_siblings, + applicable_validators=_keywords_draft3.ignore_ref_siblings, ) Draft4Validator = create( @@ -638,22 +638,22 @@ def extend( ), validators={ "$ref": _keywords.ref, - "additionalItems": _legacy_keywords.additionalItems, + "additionalItems": _keywords_draft3.additionalItems, "additionalProperties": _keywords.additionalProperties, "allOf": _keywords.allOf, "anyOf": _keywords.anyOf, - "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "dependencies": _keywords_draft3.dependencies_draft4_draft6_draft7, "enum": _keywords.enum, "format": _keywords.format, - "items": _legacy_keywords.items_draft3_draft4, + "items": _keywords_draft3.items_draft3_draft4, "maxItems": _keywords.maxItems, "maxLength": _keywords.maxLength, "maxProperties": _keywords.maxProperties, - "maximum": _legacy_keywords.maximum_draft3_draft4, + "maximum": _keywords_draft3.maximum_draft3_draft4, "minItems": _keywords.minItems, "minLength": _keywords.minLength, "minProperties": _keywords.minProperties, - "minimum": _legacy_keywords.minimum_draft3_draft4, + "minimum": _keywords_draft3.minimum_draft3_draft4, "multipleOf": _keywords.multipleOf, "not": _keywords.not_, "oneOf": _keywords.oneOf, @@ -668,7 +668,7 @@ def extend( format_checker=_format.draft4_format_checker, version="draft4", id_of=referencing.jsonschema.DRAFT4.id_of, - applicable_validators=_legacy_keywords.ignore_ref_siblings, + applicable_validators=_keywords_draft3.ignore_ref_siblings, ) Draft6Validator = create( @@ -677,18 +677,18 @@ def extend( ), validators={ "$ref": _keywords.ref, - "additionalItems": _legacy_keywords.additionalItems, + "additionalItems": _keywords_draft3.additionalItems, "additionalProperties": _keywords.additionalProperties, "allOf": _keywords.allOf, "anyOf": _keywords.anyOf, "const": _keywords.const, - "contains": _legacy_keywords.contains_draft6_draft7, - "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "contains": _keywords_draft3.contains_draft6_draft7, + "dependencies": _keywords_draft3.dependencies_draft4_draft6_draft7, "enum": _keywords.enum, "exclusiveMaximum": _keywords.exclusiveMaximum, "exclusiveMinimum": _keywords.exclusiveMinimum, "format": _keywords.format, - "items": _legacy_keywords.items_draft6_draft7_draft201909, + "items": _keywords_draft3.items_draft6_draft7_draft201909, "maxItems": _keywords.maxItems, "maxLength": _keywords.maxLength, "maxProperties": _keywords.maxProperties, @@ -712,7 +712,7 @@ def extend( format_checker=_format.draft6_format_checker, version="draft6", id_of=referencing.jsonschema.DRAFT6.id_of, - applicable_validators=_legacy_keywords.ignore_ref_siblings, + applicable_validators=_keywords_draft3.ignore_ref_siblings, ) Draft7Validator = create( @@ -721,19 +721,19 @@ def extend( ), validators={ "$ref": _keywords.ref, - "additionalItems": _legacy_keywords.additionalItems, + "additionalItems": _keywords_draft3.additionalItems, "additionalProperties": _keywords.additionalProperties, "allOf": _keywords.allOf, "anyOf": _keywords.anyOf, "const": _keywords.const, - "contains": _legacy_keywords.contains_draft6_draft7, - "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, + "contains": _keywords_draft3.contains_draft6_draft7, + "dependencies": _keywords_draft3.dependencies_draft4_draft6_draft7, "enum": _keywords.enum, "exclusiveMaximum": _keywords.exclusiveMaximum, "exclusiveMinimum": _keywords.exclusiveMinimum, "format": _keywords.format, "if": _keywords.if_, - "items": _legacy_keywords.items_draft6_draft7_draft201909, + "items": _keywords_draft3.items_draft6_draft7_draft201909, "maxItems": _keywords.maxItems, "maxLength": _keywords.maxLength, "maxProperties": _keywords.maxProperties, @@ -757,7 +757,7 @@ def extend( format_checker=_format.draft7_format_checker, version="draft7", id_of=referencing.jsonschema.DRAFT7.id_of, - applicable_validators=_legacy_keywords.ignore_ref_siblings, + applicable_validators=_keywords_draft3.ignore_ref_siblings, ) Draft201909Validator = create( @@ -765,9 +765,9 @@ def extend( "https://json-schema.org/draft/2019-09/schema", ), validators={ - "$recursiveRef": _legacy_keywords.recursiveRef, + "$recursiveRef": _keywords_draft3.recursiveRef, "$ref": _keywords.ref, - "additionalItems": _legacy_keywords.additionalItems, + "additionalItems": _keywords_draft3.additionalItems, "additionalProperties": _keywords.additionalProperties, "allOf": _keywords.allOf, "anyOf": _keywords.anyOf, @@ -780,7 +780,7 @@ def extend( "exclusiveMinimum": _keywords.exclusiveMinimum, "format": _keywords.format, "if": _keywords.if_, - "items": _legacy_keywords.items_draft6_draft7_draft201909, + "items": _keywords_draft3.items_draft6_draft7_draft201909, "maxItems": _keywords.maxItems, "maxLength": _keywords.maxLength, "maxProperties": _keywords.maxProperties, @@ -798,9 +798,9 @@ def extend( "propertyNames": _keywords.propertyNames, "required": _keywords.required, "type": _keywords.type, - "unevaluatedItems": _legacy_keywords.unevaluatedItems_draft2019, + "unevaluatedItems": _keywords_draft3.unevaluatedItems_draft2019, "unevaluatedProperties": ( - _legacy_keywords.unevaluatedProperties_draft2019 + _keywords_draft3.unevaluatedProperties_draft2019 ), "uniqueItems": _keywords.uniqueItems, },