Spaces:
Paused
Paused
Download venv/lib/python3.11/site-packages/jsonschema/validators.py from Tejasw1/Statute_Prediction: direct link, hf CLI and curl.
- Browser
- Download file 46.5 kB
-
https://huggingface.co/spaces/Tejasw1/Statute_Prediction/resolve/main/venv/lib/python3.11/site-packages/jsonschema/validators.py
- Command line
-
hf download hf://spaces/Tejasw1/Statute_Prediction/venv/lib/python3.11/site-packages/jsonschema/validators.py
-
curl -L -o validators.py https://huggingface.co/spaces/Tejasw1/Statute_Prediction/resolve/main/venv/lib/python3.11/site-packages/jsonschema/validators.py
46.5 kB
| """ | |
| Creation and extension of validators, with implementations for existing drafts. | |
| """ | |
| from __future__ import annotations | |
| from collections import deque | |
| from collections.abc import Iterable, Mapping, Sequence | |
| from functools import lru_cache | |
| from operator import methodcaller | |
| from typing import TYPE_CHECKING | |
| from urllib.parse import unquote, urldefrag, urljoin, urlsplit | |
| from urllib.request import urlopen | |
| from warnings import warn | |
| import contextlib | |
| import json | |
| import reprlib | |
| import warnings | |
| from attrs import define, field, fields | |
| from jsonschema_specifications import REGISTRY as SPECIFICATIONS | |
| from rpds import HashTrieMap | |
| import referencing.exceptions | |
| import referencing.jsonschema | |
| from jsonschema import ( | |
| _format, | |
| _keywords, | |
| _legacy_keywords, | |
| _types, | |
| _typing, | |
| _utils, | |
| exceptions, | |
| ) | |
| if TYPE_CHECKING: | |
| from jsonschema.protocols import Validator | |
| _UNSET = _utils.Unset() | |
| _VALIDATORS: dict[str, Validator] = {} | |
| _META_SCHEMAS = _utils.URIDict() | |
| def __getattr__(name): | |
| if name == "ErrorTree": | |
| warnings.warn( | |
| "Importing ErrorTree from jsonschema.validators is deprecated. " | |
| "Instead import it from jsonschema.exceptions.", | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| from jsonschema.exceptions import ErrorTree | |
| return ErrorTree | |
| elif name == "validators": | |
| warnings.warn( | |
| "Accessing jsonschema.validators.validators is deprecated. " | |
| "Use jsonschema.validators.validator_for with a given schema.", | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| return _VALIDATORS | |
| elif name == "meta_schemas": | |
| warnings.warn( | |
| "Accessing jsonschema.validators.meta_schemas is deprecated. " | |
| "Use jsonschema.validators.validator_for with a given schema.", | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| return _META_SCHEMAS | |
| elif name == "RefResolver": | |
| warnings.warn( | |
| _RefResolver._DEPRECATION_MESSAGE, | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| return _RefResolver | |
| raise AttributeError(f"module {__name__} has no attribute {name}") | |
| def validates(version): | |
| """ | |
| Register the decorated validator for a ``version`` of the specification. | |
| Registered validators and their meta schemas will be considered when | |
| parsing :kw:`$schema` keywords' URIs. | |
| Arguments: | |
| version (str): | |
| An identifier to use as the version's name | |
| Returns: | |
| collections.abc.Callable: | |
| a class decorator to decorate the validator with the version | |
| """ | |
| def _validates(cls): | |
| _VALIDATORS[version] = cls | |
| meta_schema_id = cls.ID_OF(cls.META_SCHEMA) | |
| _META_SCHEMAS[meta_schema_id] = cls | |
| return cls | |
| return _validates | |
| def _warn_for_remote_retrieve(uri: str): | |
| from urllib.request import Request, urlopen | |
| headers = {"User-Agent": "python-jsonschema (deprecated $ref resolution)"} | |
| request = Request(uri, headers=headers) # noqa: S310 | |
| with urlopen(request) as response: # noqa: S310 | |
| warnings.warn( | |
| "Automatically retrieving remote references can be a security " | |
| "vulnerability and is discouraged by the JSON Schema " | |
| "specifications. Relying on this behavior is deprecated " | |
| "and will shortly become an error. If you are sure you want to " | |
| "remotely retrieve your reference and that it is safe to do so, " | |
| "you can find instructions for doing so via referencing.Registry " | |
| "in the referencing documentation " | |
| "(https://referencing.readthedocs.org).", | |
| DeprecationWarning, | |
| stacklevel=9, # Ha ha ha ha magic numbers :/ | |
| ) | |
| return referencing.Resource.from_contents( | |
| json.load(response), | |
| default_specification=referencing.jsonschema.DRAFT202012, | |
| ) | |
| _REMOTE_WARNING_REGISTRY = SPECIFICATIONS.combine( | |
| referencing.Registry(retrieve=_warn_for_remote_retrieve), # type: ignore[call-arg] | |
| ) | |
| def create( | |
| meta_schema: referencing.jsonschema.ObjectSchema, | |
| validators: ( | |
| Mapping[str, _typing.SchemaKeywordValidator] | |
| | Iterable[tuple[str, _typing.SchemaKeywordValidator]] | |
| ) = (), | |
| version: str | None = None, | |
| type_checker: _types.TypeChecker = _types.draft202012_type_checker, | |
| format_checker: _format.FormatChecker = _format.draft202012_format_checker, | |
| id_of: _typing.id_of = referencing.jsonschema.DRAFT202012.id_of, | |
| applicable_validators: _typing.ApplicableValidators = methodcaller( | |
| "items", | |
| ), | |
| ): | |
| """ | |
| Create a new validator class. | |
| Arguments: | |
| meta_schema: | |
| the meta schema for the new validator class | |
| validators: | |
| a mapping from names to callables, where each callable will | |
| validate the schema property with the given name. | |
| Each callable should take 4 arguments: | |
| 1. a validator instance, | |
| 2. the value of the property being validated within the | |
| instance | |
| 3. the instance | |
| 4. the schema | |
| version: | |
| an identifier for the version that this validator class will | |
| validate. If provided, the returned validator class will | |
| have its ``__name__`` set to include the version, and also | |
| will have `jsonschema.validators.validates` automatically | |
| called for the given version. | |
| type_checker: | |
| a type checker, used when applying the :kw:`type` keyword. | |
| If unprovided, a `jsonschema.TypeChecker` will be created | |
| with a set of default types typical of JSON Schema drafts. | |
| format_checker: | |
| a format checker, used when applying the :kw:`format` keyword. | |
| If unprovided, a `jsonschema.FormatChecker` will be created | |
| with a set of default formats typical of JSON Schema drafts. | |
| id_of: | |
| A function that given a schema, returns its ID. | |
| applicable_validators: | |
| A function that, given a schema, returns the list of | |
| applicable schema keywords and associated values | |
| which will be used to validate the instance. | |
| This is mostly used to support pre-draft 7 versions of JSON Schema | |
| which specified behavior around ignoring keywords if they were | |
| siblings of a ``$ref`` keyword. If you're not attempting to | |
| implement similar behavior, you can typically ignore this argument | |
| and leave it at its default. | |
| Returns: | |
| a new `jsonschema.protocols.Validator` class | |
| """ | |
| # preemptively don't shadow the `Validator.format_checker` local | |
| format_checker_arg = format_checker | |
| specification = referencing.jsonschema.specification_with( | |
| dialect_id=id_of(meta_schema) or "urn:unknown-dialect", | |
| default=referencing.Specification.OPAQUE, | |
| ) | |
| class Validator: | |
| VALIDATORS = dict(validators) # noqa: RUF012 | |
| META_SCHEMA = dict(meta_schema) # noqa: RUF012 | |
| TYPE_CHECKER = type_checker | |
| FORMAT_CHECKER = format_checker_arg | |
| ID_OF = staticmethod(id_of) | |
| _APPLICABLE_VALIDATORS = applicable_validators | |
| schema: referencing.jsonschema.Schema = field(repr=reprlib.repr) | |
| _ref_resolver = field(default=None, repr=False, alias="resolver") | |
| format_checker: _format.FormatChecker | None = field(default=None) | |
| # TODO: include new meta-schemas added at runtime | |
| _registry: referencing.jsonschema.SchemaRegistry = field( | |
| default=_REMOTE_WARNING_REGISTRY, | |
| kw_only=True, | |
| repr=False, | |
| ) | |
| _resolver = field( | |
| alias="_resolver", | |
| default=None, | |
| kw_only=True, | |
| repr=False, | |
| ) | |
| def __init_subclass__(cls): | |
| warnings.warn( | |
| ( | |
| "Subclassing validator classes is not intended to " | |
| "be part of their public API. A future version " | |
| "will make doing so an error, as the behavior of " | |
| "subclasses isn't guaranteed to stay the same " | |
| "between releases of jsonschema. Instead, prefer " | |
| "composition of validators, wrapping them in an object " | |
| "owned entirely by the downstream library." | |
| ), | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| def evolve(self, **changes): | |
| cls = self.__class__ | |
| schema = changes.setdefault("schema", self.schema) | |
| NewValidator = validator_for(schema, default=cls) | |
| for field in fields(cls): # noqa: F402 | |
| if not field.init: | |
| continue | |
| attr_name = field.name | |
| init_name = field.alias | |
| if init_name not in changes: | |
| changes[init_name] = getattr(self, attr_name) | |
| return NewValidator(**changes) | |
| cls.evolve = evolve | |
| def __attrs_post_init__(self): | |
| if self._resolver is None: | |
| registry = self._registry | |
| if registry is not _REMOTE_WARNING_REGISTRY: | |
| registry = SPECIFICATIONS.combine(registry) | |
| resource = specification.create_resource(self.schema) | |
| self._resolver = registry.resolver_with_root(resource) | |
| # REMOVEME: Legacy ref resolution state management. | |
| push_scope = getattr(self._ref_resolver, "push_scope", None) | |
| if push_scope is not None: | |
| id = id_of(self.schema) | |
| if id is not None: | |
| push_scope(id) | |
| def check_schema(cls, schema, format_checker=_UNSET): | |
| Validator = validator_for(cls.META_SCHEMA, default=cls) | |
| if format_checker is _UNSET: | |
| format_checker = Validator.FORMAT_CHECKER | |
| validator = Validator( | |
| schema=cls.META_SCHEMA, | |
| format_checker=format_checker, | |
| ) | |
| for error in validator.iter_errors(schema): | |
| raise exceptions.SchemaError.create_from(error) | |
| def resolver(self): | |
| warnings.warn( | |
| ( | |
| f"Accessing {self.__class__.__name__}.resolver is " | |
| "deprecated as of v4.18.0, in favor of the " | |
| "https://github.com/python-jsonschema/referencing " | |
| "library, which provides more compliant referencing " | |
| "behavior as well as more flexible APIs for " | |
| "customization." | |
| ), | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| if self._ref_resolver is None: | |
| self._ref_resolver = _RefResolver.from_schema( | |
| self.schema, | |
| id_of=id_of, | |
| ) | |
| return self._ref_resolver | |
| def evolve(self, **changes): | |
| schema = changes.setdefault("schema", self.schema) | |
| NewValidator = validator_for(schema, default=self.__class__) | |
| for (attr_name, init_name) in evolve_fields: | |
| if init_name not in changes: | |
| changes[init_name] = getattr(self, attr_name) | |
| return NewValidator(**changes) | |
| def iter_errors(self, instance, _schema=None): | |
| if _schema is not None: | |
| warnings.warn( | |
| ( | |
| "Passing a schema to Validator.iter_errors " | |
| "is deprecated and will be removed in a future " | |
| "release. Call validator.evolve(schema=new_schema)." | |
| "iter_errors(...) instead." | |
| ), | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| else: | |
| _schema = self.schema | |
| if _schema is True: | |
| return | |
| elif _schema is False: | |
| yield exceptions.ValidationError( | |
| f"False schema does not allow {instance!r}", | |
| validator=None, | |
| validator_value=None, | |
| instance=instance, | |
| schema=_schema, | |
| ) | |
| return | |
| for k, v in applicable_validators(_schema): | |
| validator = self.VALIDATORS.get(k) | |
| if validator is None: | |
| continue | |
| errors = validator(self, v, instance, _schema) or () | |
| for error in errors: | |
| # set details if not already set by the called fn | |
| error._set( | |
| validator=k, | |
| validator_value=v, | |
| instance=instance, | |
| schema=_schema, | |
| type_checker=self.TYPE_CHECKER, | |
| ) | |
| if k not in {"if", "$ref"}: | |
| error.schema_path.appendleft(k) | |
| yield error | |
| def descend( | |
| self, | |
| instance, | |
| schema, | |
| path=None, | |
| schema_path=None, | |
| resolver=None, | |
| ): | |
| if schema is True: | |
| return | |
| elif schema is False: | |
| yield exceptions.ValidationError( | |
| f"False schema does not allow {instance!r}", | |
| validator=None, | |
| validator_value=None, | |
| instance=instance, | |
| schema=schema, | |
| ) | |
| return | |
| if self._ref_resolver is not None: | |
| evolved = self.evolve(schema=schema) | |
| else: | |
| if resolver is None: | |
| resolver = self._resolver.in_subresource( | |
| specification.create_resource(schema), | |
| ) | |
| evolved = self.evolve(schema=schema, _resolver=resolver) | |
| for k, v in applicable_validators(schema): | |
| validator = evolved.VALIDATORS.get(k) | |
| if validator is None: | |
| continue | |
| errors = validator(evolved, v, instance, schema) or () | |
| for error in errors: | |
| # set details if not already set by the called fn | |
| error._set( | |
| validator=k, | |
| validator_value=v, | |
| instance=instance, | |
| schema=schema, | |
| type_checker=evolved.TYPE_CHECKER, | |
| ) | |
| if k not in {"if", "$ref"}: | |
| error.schema_path.appendleft(k) | |
| if path is not None: | |
| error.path.appendleft(path) | |
| if schema_path is not None: | |
| error.schema_path.appendleft(schema_path) | |
| yield error | |
| def validate(self, *args, **kwargs): | |
| for error in self.iter_errors(*args, **kwargs): | |
| raise error | |
| def is_type(self, instance, type): | |
| try: | |
| return self.TYPE_CHECKER.is_type(instance, type) | |
| except exceptions.UndefinedTypeCheck: | |
| exc = exceptions.UnknownType(type, instance, self.schema) | |
| raise exc from None | |
| def _validate_reference(self, ref, instance): | |
| if self._ref_resolver is None: | |
| try: | |
| resolved = self._resolver.lookup(ref) | |
| except referencing.exceptions.Unresolvable as err: | |
| raise exceptions._WrappedReferencingError(err) from err | |
| return self.descend( | |
| instance, | |
| resolved.contents, | |
| resolver=resolved.resolver, | |
| ) | |
| else: | |
| resolve = getattr(self._ref_resolver, "resolve", None) | |
| if resolve is None: | |
| with self._ref_resolver.resolving(ref) as resolved: | |
| return self.descend(instance, resolved) | |
| else: | |
| scope, resolved = resolve(ref) | |
| self._ref_resolver.push_scope(scope) | |
| try: | |
| return list(self.descend(instance, resolved)) | |
| finally: | |
| self._ref_resolver.pop_scope() | |
| def is_valid(self, instance, _schema=None): | |
| if _schema is not None: | |
| warnings.warn( | |
| ( | |
| "Passing a schema to Validator.is_valid is deprecated " | |
| "and will be removed in a future release. Call " | |
| "validator.evolve(schema=new_schema).is_valid(...) " | |
| "instead." | |
| ), | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| self = self.evolve(schema=_schema) | |
| error = next(self.iter_errors(instance), None) | |
| return error is None | |
| evolve_fields = [ | |
| (field.name, field.alias) | |
| for field in fields(Validator) | |
| if field.init | |
| ] | |
| if version is not None: | |
| safe = version.title().replace(" ", "").replace("-", "") | |
| Validator.__name__ = Validator.__qualname__ = f"{safe}Validator" | |
| Validator = validates(version)(Validator) # type: ignore[misc] | |
| return Validator | |
| def extend( | |
| validator, | |
| validators=(), | |
| version=None, | |
| type_checker=None, | |
| format_checker=None, | |
| ): | |
| """ | |
| Create a new validator class by extending an existing one. | |
| Arguments: | |
| validator (jsonschema.protocols.Validator): | |
| an existing validator class | |
| validators (collections.abc.Mapping): | |
| a mapping of new validator callables to extend with, whose | |
| structure is as in `create`. | |
| .. note:: | |
| Any validator callables with the same name as an | |
| existing one will (silently) replace the old validator | |
| callable entirely, effectively overriding any validation | |
| done in the "parent" validator class. | |
| If you wish to instead extend the behavior of a parent's | |
| validator callable, delegate and call it directly in | |
| the new validator function by retrieving it using | |
| ``OldValidator.VALIDATORS["validation_keyword_name"]``. | |
| version (str): | |
| a version for the new validator class | |
| type_checker (jsonschema.TypeChecker): | |
| a type checker, used when applying the :kw:`type` keyword. | |
| If unprovided, the type checker of the extended | |
| `jsonschema.protocols.Validator` will be carried along. | |
| format_checker (jsonschema.FormatChecker): | |
| a format checker, used when applying the :kw:`format` keyword. | |
| If unprovided, the format checker of the extended | |
| `jsonschema.protocols.Validator` will be carried along. | |
| Returns: | |
| a new `jsonschema.protocols.Validator` class extending the one | |
| provided | |
| .. note:: Meta Schemas | |
| The new validator class will have its parent's meta schema. | |
| If you wish to change or extend the meta schema in the new | |
| validator class, modify ``META_SCHEMA`` directly on the returned | |
| class. Note that no implicit copying is done, so a copy should | |
| likely be made before modifying it, in order to not affect the | |
| old validator. | |
| """ | |
| all_validators = dict(validator.VALIDATORS) | |
| all_validators.update(validators) | |
| if type_checker is None: | |
| type_checker = validator.TYPE_CHECKER | |
| if format_checker is None: | |
| format_checker = validator.FORMAT_CHECKER | |
| return create( | |
| meta_schema=validator.META_SCHEMA, | |
| validators=all_validators, | |
| version=version, | |
| type_checker=type_checker, | |
| format_checker=format_checker, | |
| id_of=validator.ID_OF, | |
| applicable_validators=validator._APPLICABLE_VALIDATORS, | |
| ) | |
| Draft3Validator = create( | |
| meta_schema=SPECIFICATIONS.contents( | |
| "http://json-schema.org/draft-03/schema#", | |
| ), | |
| validators={ | |
| "$ref": _keywords.ref, | |
| "additionalItems": _legacy_keywords.additionalItems, | |
| "additionalProperties": _keywords.additionalProperties, | |
| "dependencies": _legacy_keywords.dependencies_draft3, | |
| "disallow": _legacy_keywords.disallow_draft3, | |
| "divisibleBy": _keywords.multipleOf, | |
| "enum": _keywords.enum, | |
| "extends": _legacy_keywords.extends_draft3, | |
| "format": _keywords.format, | |
| "items": _legacy_keywords.items_draft3_draft4, | |
| "maxItems": _keywords.maxItems, | |
| "maxLength": _keywords.maxLength, | |
| "maximum": _legacy_keywords.maximum_draft3_draft4, | |
| "minItems": _keywords.minItems, | |
| "minLength": _keywords.minLength, | |
| "minimum": _legacy_keywords.minimum_draft3_draft4, | |
| "pattern": _keywords.pattern, | |
| "patternProperties": _keywords.patternProperties, | |
| "properties": _legacy_keywords.properties_draft3, | |
| "type": _legacy_keywords.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, | |
| ) | |
| Draft4Validator = create( | |
| meta_schema=SPECIFICATIONS.contents( | |
| "http://json-schema.org/draft-04/schema#", | |
| ), | |
| validators={ | |
| "$ref": _keywords.ref, | |
| "additionalItems": _legacy_keywords.additionalItems, | |
| "additionalProperties": _keywords.additionalProperties, | |
| "allOf": _keywords.allOf, | |
| "anyOf": _keywords.anyOf, | |
| "dependencies": _legacy_keywords.dependencies_draft4_draft6_draft7, | |
| "enum": _keywords.enum, | |
| "format": _keywords.format, | |
| "items": _legacy_keywords.items_draft3_draft4, | |
| "maxItems": _keywords.maxItems, | |
| "maxLength": _keywords.maxLength, | |
| "maxProperties": _keywords.maxProperties, | |
| "maximum": _legacy_keywords.maximum_draft3_draft4, | |
| "minItems": _keywords.minItems, | |
| "minLength": _keywords.minLength, | |
| "minProperties": _keywords.minProperties, | |
| "minimum": _legacy_keywords.minimum_draft3_draft4, | |
| "multipleOf": _keywords.multipleOf, | |
| "not": _keywords.not_, | |
| "oneOf": _keywords.oneOf, | |
| "pattern": _keywords.pattern, | |
| "patternProperties": _keywords.patternProperties, | |
| "properties": _keywords.properties, | |
| "required": _keywords.required, | |
| "type": _keywords.type, | |
| "uniqueItems": _keywords.uniqueItems, | |
| }, | |
| type_checker=_types.draft4_type_checker, | |
| format_checker=_format.draft4_format_checker, | |
| version="draft4", | |
| id_of=referencing.jsonschema.DRAFT4.id_of, | |
| applicable_validators=_legacy_keywords.ignore_ref_siblings, | |
| ) | |
| Draft6Validator = create( | |
| meta_schema=SPECIFICATIONS.contents( | |
| "http://json-schema.org/draft-06/schema#", | |
| ), | |
| validators={ | |
| "$ref": _keywords.ref, | |
| "additionalItems": _legacy_keywords.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, | |
| "enum": _keywords.enum, | |
| "exclusiveMaximum": _keywords.exclusiveMaximum, | |
| "exclusiveMinimum": _keywords.exclusiveMinimum, | |
| "format": _keywords.format, | |
| "items": _legacy_keywords.items_draft6_draft7_draft201909, | |
| "maxItems": _keywords.maxItems, | |
| "maxLength": _keywords.maxLength, | |
| "maxProperties": _keywords.maxProperties, | |
| "maximum": _keywords.maximum, | |
| "minItems": _keywords.minItems, | |
| "minLength": _keywords.minLength, | |
| "minProperties": _keywords.minProperties, | |
| "minimum": _keywords.minimum, | |
| "multipleOf": _keywords.multipleOf, | |
| "not": _keywords.not_, | |
| "oneOf": _keywords.oneOf, | |
| "pattern": _keywords.pattern, | |
| "patternProperties": _keywords.patternProperties, | |
| "properties": _keywords.properties, | |
| "propertyNames": _keywords.propertyNames, | |
| "required": _keywords.required, | |
| "type": _keywords.type, | |
| "uniqueItems": _keywords.uniqueItems, | |
| }, | |
| type_checker=_types.draft6_type_checker, | |
| format_checker=_format.draft6_format_checker, | |
| version="draft6", | |
| id_of=referencing.jsonschema.DRAFT6.id_of, | |
| applicable_validators=_legacy_keywords.ignore_ref_siblings, | |
| ) | |
| Draft7Validator = create( | |
| meta_schema=SPECIFICATIONS.contents( | |
| "http://json-schema.org/draft-07/schema#", | |
| ), | |
| validators={ | |
| "$ref": _keywords.ref, | |
| "additionalItems": _legacy_keywords.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, | |
| "enum": _keywords.enum, | |
| "exclusiveMaximum": _keywords.exclusiveMaximum, | |
| "exclusiveMinimum": _keywords.exclusiveMinimum, | |
| "format": _keywords.format, | |
| "if": _keywords.if_, | |
| "items": _legacy_keywords.items_draft6_draft7_draft201909, | |
| "maxItems": _keywords.maxItems, | |
| "maxLength": _keywords.maxLength, | |
| "maxProperties": _keywords.maxProperties, | |
| "maximum": _keywords.maximum, | |
| "minItems": _keywords.minItems, | |
| "minLength": _keywords.minLength, | |
| "minProperties": _keywords.minProperties, | |
| "minimum": _keywords.minimum, | |
| "multipleOf": _keywords.multipleOf, | |
| "not": _keywords.not_, | |
| "oneOf": _keywords.oneOf, | |
| "pattern": _keywords.pattern, | |
| "patternProperties": _keywords.patternProperties, | |
| "properties": _keywords.properties, | |
| "propertyNames": _keywords.propertyNames, | |
| "required": _keywords.required, | |
| "type": _keywords.type, | |
| "uniqueItems": _keywords.uniqueItems, | |
| }, | |
| type_checker=_types.draft7_type_checker, | |
| format_checker=_format.draft7_format_checker, | |
| version="draft7", | |
| id_of=referencing.jsonschema.DRAFT7.id_of, | |
| applicable_validators=_legacy_keywords.ignore_ref_siblings, | |
| ) | |
| Draft201909Validator = create( | |
| meta_schema=SPECIFICATIONS.contents( | |
| "https://json-schema.org/draft/2019-09/schema", | |
| ), | |
| validators={ | |
| "$recursiveRef": _legacy_keywords.recursiveRef, | |
| "$ref": _keywords.ref, | |
| "additionalItems": _legacy_keywords.additionalItems, | |
| "additionalProperties": _keywords.additionalProperties, | |
| "allOf": _keywords.allOf, | |
| "anyOf": _keywords.anyOf, | |
| "const": _keywords.const, | |
| "contains": _keywords.contains, | |
| "dependentRequired": _keywords.dependentRequired, | |
| "dependentSchemas": _keywords.dependentSchemas, | |
| "enum": _keywords.enum, | |
| "exclusiveMaximum": _keywords.exclusiveMaximum, | |
| "exclusiveMinimum": _keywords.exclusiveMinimum, | |
| "format": _keywords.format, | |
| "if": _keywords.if_, | |
| "items": _legacy_keywords.items_draft6_draft7_draft201909, | |
| "maxItems": _keywords.maxItems, | |
| "maxLength": _keywords.maxLength, | |
| "maxProperties": _keywords.maxProperties, | |
| "maximum": _keywords.maximum, | |
| "minItems": _keywords.minItems, | |
| "minLength": _keywords.minLength, | |
| "minProperties": _keywords.minProperties, | |
| "minimum": _keywords.minimum, | |
| "multipleOf": _keywords.multipleOf, | |
| "not": _keywords.not_, | |
| "oneOf": _keywords.oneOf, | |
| "pattern": _keywords.pattern, | |
| "patternProperties": _keywords.patternProperties, | |
| "properties": _keywords.properties, | |
| "propertyNames": _keywords.propertyNames, | |
| "required": _keywords.required, | |
| "type": _keywords.type, | |
| "unevaluatedItems": _legacy_keywords.unevaluatedItems_draft2019, | |
| "unevaluatedProperties": ( | |
| _legacy_keywords.unevaluatedProperties_draft2019 | |
| ), | |
| "uniqueItems": _keywords.uniqueItems, | |
| }, | |
| type_checker=_types.draft201909_type_checker, | |
| format_checker=_format.draft201909_format_checker, | |
| version="draft2019-09", | |
| ) | |
| Draft202012Validator = create( | |
| meta_schema=SPECIFICATIONS.contents( | |
| "https://json-schema.org/draft/2020-12/schema", | |
| ), | |
| validators={ | |
| "$dynamicRef": _keywords.dynamicRef, | |
| "$ref": _keywords.ref, | |
| "additionalProperties": _keywords.additionalProperties, | |
| "allOf": _keywords.allOf, | |
| "anyOf": _keywords.anyOf, | |
| "const": _keywords.const, | |
| "contains": _keywords.contains, | |
| "dependentRequired": _keywords.dependentRequired, | |
| "dependentSchemas": _keywords.dependentSchemas, | |
| "enum": _keywords.enum, | |
| "exclusiveMaximum": _keywords.exclusiveMaximum, | |
| "exclusiveMinimum": _keywords.exclusiveMinimum, | |
| "format": _keywords.format, | |
| "if": _keywords.if_, | |
| "items": _keywords.items, | |
| "maxItems": _keywords.maxItems, | |
| "maxLength": _keywords.maxLength, | |
| "maxProperties": _keywords.maxProperties, | |
| "maximum": _keywords.maximum, | |
| "minItems": _keywords.minItems, | |
| "minLength": _keywords.minLength, | |
| "minProperties": _keywords.minProperties, | |
| "minimum": _keywords.minimum, | |
| "multipleOf": _keywords.multipleOf, | |
| "not": _keywords.not_, | |
| "oneOf": _keywords.oneOf, | |
| "pattern": _keywords.pattern, | |
| "patternProperties": _keywords.patternProperties, | |
| "prefixItems": _keywords.prefixItems, | |
| "properties": _keywords.properties, | |
| "propertyNames": _keywords.propertyNames, | |
| "required": _keywords.required, | |
| "type": _keywords.type, | |
| "unevaluatedItems": _keywords.unevaluatedItems, | |
| "unevaluatedProperties": _keywords.unevaluatedProperties, | |
| "uniqueItems": _keywords.uniqueItems, | |
| }, | |
| type_checker=_types.draft202012_type_checker, | |
| format_checker=_format.draft202012_format_checker, | |
| version="draft2020-12", | |
| ) | |
| _LATEST_VERSION = Draft202012Validator | |
| class _RefResolver: | |
| """ | |
| Resolve JSON References. | |
| Arguments: | |
| base_uri (str): | |
| The URI of the referring document | |
| referrer: | |
| The actual referring document | |
| store (dict): | |
| A mapping from URIs to documents to cache | |
| cache_remote (bool): | |
| Whether remote refs should be cached after first resolution | |
| handlers (dict): | |
| A mapping from URI schemes to functions that should be used | |
| to retrieve them | |
| urljoin_cache (:func:`functools.lru_cache`): | |
| A cache that will be used for caching the results of joining | |
| the resolution scope to subscopes. | |
| remote_cache (:func:`functools.lru_cache`): | |
| A cache that will be used for caching the results of | |
| resolved remote URLs. | |
| Attributes: | |
| cache_remote (bool): | |
| Whether remote refs should be cached after first resolution | |
| .. deprecated:: v4.18.0 | |
| ``RefResolver`` has been deprecated in favor of `referencing`. | |
| """ | |
| _DEPRECATION_MESSAGE = ( | |
| "jsonschema.RefResolver is deprecated as of v4.18.0, in favor of the " | |
| "https://github.com/python-jsonschema/referencing library, which " | |
| "provides more compliant referencing behavior as well as more " | |
| "flexible APIs for customization. A future release will remove " | |
| "RefResolver. Please file a feature request (on referencing) if you " | |
| "are missing an API for the kind of customization you need." | |
| ) | |
| def __init__( | |
| self, | |
| base_uri, | |
| referrer, | |
| store=HashTrieMap(), | |
| cache_remote=True, | |
| handlers=(), | |
| urljoin_cache=None, | |
| remote_cache=None, | |
| ): | |
| if urljoin_cache is None: | |
| urljoin_cache = lru_cache(1024)(urljoin) | |
| if remote_cache is None: | |
| remote_cache = lru_cache(1024)(self.resolve_from_url) | |
| self.referrer = referrer | |
| self.cache_remote = cache_remote | |
| self.handlers = dict(handlers) | |
| self._scopes_stack = [base_uri] | |
| self.store = _utils.URIDict( | |
| (uri, each.contents) for uri, each in SPECIFICATIONS.items() | |
| ) | |
| self.store.update( | |
| (id, each.META_SCHEMA) for id, each in _META_SCHEMAS.items() | |
| ) | |
| self.store.update(store) | |
| self.store.update( | |
| (schema["$id"], schema) | |
| for schema in store.values() | |
| if isinstance(schema, Mapping) and "$id" in schema | |
| ) | |
| self.store[base_uri] = referrer | |
| self._urljoin_cache = urljoin_cache | |
| self._remote_cache = remote_cache | |
| def from_schema( # noqa: D417 | |
| cls, | |
| schema, | |
| id_of=referencing.jsonschema.DRAFT202012.id_of, | |
| *args, | |
| **kwargs, | |
| ): | |
| """ | |
| Construct a resolver from a JSON schema object. | |
| Arguments: | |
| schema: | |
| the referring schema | |
| Returns: | |
| `_RefResolver` | |
| """ | |
| return cls(base_uri=id_of(schema) or "", referrer=schema, *args, **kwargs) # noqa: B026, E501 | |
| def push_scope(self, scope): | |
| """ | |
| Enter a given sub-scope. | |
| Treats further dereferences as being performed underneath the | |
| given scope. | |
| """ | |
| self._scopes_stack.append( | |
| self._urljoin_cache(self.resolution_scope, scope), | |
| ) | |
| def pop_scope(self): | |
| """ | |
| Exit the most recent entered scope. | |
| Treats further dereferences as being performed underneath the | |
| original scope. | |
| Don't call this method more times than `push_scope` has been | |
| called. | |
| """ | |
| try: | |
| self._scopes_stack.pop() | |
| except IndexError: | |
| raise exceptions._RefResolutionError( | |
| "Failed to pop the scope from an empty stack. " | |
| "`pop_scope()` should only be called once for every " | |
| "`push_scope()`", | |
| ) from None | |
| def resolution_scope(self): | |
| """ | |
| Retrieve the current resolution scope. | |
| """ | |
| return self._scopes_stack[-1] | |
| def base_uri(self): | |
| """ | |
| Retrieve the current base URI, not including any fragment. | |
| """ | |
| uri, _ = urldefrag(self.resolution_scope) | |
| return uri | |
| def in_scope(self, scope): | |
| """ | |
| Temporarily enter the given scope for the duration of the context. | |
| .. deprecated:: v4.0.0 | |
| """ | |
| warnings.warn( | |
| "jsonschema.RefResolver.in_scope is deprecated and will be " | |
| "removed in a future release.", | |
| DeprecationWarning, | |
| stacklevel=3, | |
| ) | |
| self.push_scope(scope) | |
| try: | |
| yield | |
| finally: | |
| self.pop_scope() | |
| def resolving(self, ref): | |
| """ | |
| Resolve the given ``ref`` and enter its resolution scope. | |
| Exits the scope on exit of this context manager. | |
| Arguments: | |
| ref (str): | |
| The reference to resolve | |
| """ | |
| url, resolved = self.resolve(ref) | |
| self.push_scope(url) | |
| try: | |
| yield resolved | |
| finally: | |
| self.pop_scope() | |
| def _find_in_referrer(self, key): | |
| return self._get_subschemas_cache()[key] | |
| # noqa: B019 | |
| def _get_subschemas_cache(self): | |
| cache = {key: [] for key in _SUBSCHEMAS_KEYWORDS} | |
| for keyword, subschema in _search_schema( | |
| self.referrer, _match_subschema_keywords, | |
| ): | |
| cache[keyword].append(subschema) | |
| return cache | |
| # noqa: B019 | |
| def _find_in_subschemas(self, url): | |
| subschemas = self._get_subschemas_cache()["$id"] | |
| if not subschemas: | |
| return None | |
| uri, fragment = urldefrag(url) | |
| for subschema in subschemas: | |
| id = subschema["$id"] | |
| if not isinstance(id, str): | |
| continue | |
| target_uri = self._urljoin_cache(self.resolution_scope, id) | |
| if target_uri.rstrip("/") == uri.rstrip("/"): | |
| if fragment: | |
| subschema = self.resolve_fragment(subschema, fragment) | |
| self.store[url] = subschema | |
| return url, subschema | |
| return None | |
| def resolve(self, ref): | |
| """ | |
| Resolve the given reference. | |
| """ | |
| url = self._urljoin_cache(self.resolution_scope, ref).rstrip("/") | |
| match = self._find_in_subschemas(url) | |
| if match is not None: | |
| return match | |
| return url, self._remote_cache(url) | |
| def resolve_from_url(self, url): | |
| """ | |
| Resolve the given URL. | |
| """ | |
| url, fragment = urldefrag(url) | |
| if not url: | |
| url = self.base_uri | |
| try: | |
| document = self.store[url] | |
| except KeyError: | |
| try: | |
| document = self.resolve_remote(url) | |
| except Exception as exc: # noqa: BLE001 | |
| raise exceptions._RefResolutionError(exc) from exc | |
| return self.resolve_fragment(document, fragment) | |
| def resolve_fragment(self, document, fragment): | |
| """ | |
| Resolve a ``fragment`` within the referenced ``document``. | |
| Arguments: | |
| document: | |
| The referent document | |
| fragment (str): | |
| a URI fragment to resolve within it | |
| """ | |
| fragment = fragment.lstrip("/") | |
| if not fragment: | |
| return document | |
| if document is self.referrer: | |
| find = self._find_in_referrer | |
| else: | |
| def find(key): | |
| yield from _search_schema(document, _match_keyword(key)) | |
| for keyword in ["$anchor", "$dynamicAnchor"]: | |
| for subschema in find(keyword): | |
| if fragment == subschema[keyword]: | |
| return subschema | |
| for keyword in ["id", "$id"]: | |
| for subschema in find(keyword): | |
| if "#" + fragment == subschema[keyword]: | |
| return subschema | |
| # Resolve via path | |
| parts = unquote(fragment).split("/") if fragment else [] | |
| for part in parts: | |
| part = part.replace("~1", "/").replace("~0", "~") | |
| if isinstance(document, Sequence): | |
| try: # noqa: SIM105 | |
| part = int(part) | |
| except ValueError: | |
| pass | |
| try: | |
| document = document[part] | |
| except (TypeError, LookupError) as err: | |
| raise exceptions._RefResolutionError( | |
| f"Unresolvable JSON pointer: {fragment!r}", | |
| ) from err | |
| return document | |
| def resolve_remote(self, uri): | |
| """ | |
| Resolve a remote ``uri``. | |
| If called directly, does not check the store first, but after | |
| retrieving the document at the specified URI it will be saved in | |
| the store if :attr:`cache_remote` is True. | |
| .. note:: | |
| If the requests_ library is present, ``jsonschema`` will use it to | |
| request the remote ``uri``, so that the correct encoding is | |
| detected and used. | |
| If it isn't, or if the scheme of the ``uri`` is not ``http`` or | |
| ``https``, UTF-8 is assumed. | |
| Arguments: | |
| uri (str): | |
| The URI to resolve | |
| Returns: | |
| The retrieved document | |
| .. _requests: https://pypi.org/project/requests/ | |
| """ | |
| try: | |
| import requests | |
| except ImportError: | |
| requests = None | |
| scheme = urlsplit(uri).scheme | |
| if scheme in self.handlers: | |
| result = self.handlers[scheme](uri) | |
| elif scheme in ["http", "https"] and requests: | |
| # Requests has support for detecting the correct encoding of | |
| # json over http | |
| result = requests.get(uri).json() | |
| else: | |
| # Otherwise, pass off to urllib and assume utf-8 | |
| with urlopen(uri) as url: # noqa: S310 | |
| result = json.loads(url.read().decode("utf-8")) | |
| if self.cache_remote: | |
| self.store[uri] = result | |
| return result | |
| _SUBSCHEMAS_KEYWORDS = ("$id", "id", "$anchor", "$dynamicAnchor") | |
| def _match_keyword(keyword): | |
| def matcher(value): | |
| if keyword in value: | |
| yield value | |
| return matcher | |
| def _match_subschema_keywords(value): | |
| for keyword in _SUBSCHEMAS_KEYWORDS: | |
| if keyword in value: | |
| yield keyword, value | |
| def _search_schema(schema, matcher): | |
| """Breadth-first search routine.""" | |
| values = deque([schema]) | |
| while values: | |
| value = values.pop() | |
| if not isinstance(value, dict): | |
| continue | |
| yield from matcher(value) | |
| values.extendleft(value.values()) | |
| def validate(instance, schema, cls=None, *args, **kwargs): # noqa: D417 | |
| """ | |
| Validate an instance under the given schema. | |
| >>> validate([2, 3, 4], {"maxItems": 2}) | |
| Traceback (most recent call last): | |
| ... | |
| ValidationError: [2, 3, 4] is too long | |
| :func:`~jsonschema.validators.validate` will first verify that the | |
| provided schema is itself valid, since not doing so can lead to less | |
| obvious error messages and fail in less obvious or consistent ways. | |
| If you know you have a valid schema already, especially | |
| if you intend to validate multiple instances with | |
| the same schema, you likely would prefer using the | |
| `jsonschema.protocols.Validator.validate` method directly on a | |
| specific validator (e.g. ``Draft202012Validator.validate``). | |
| Arguments: | |
| instance: | |
| The instance to validate | |
| schema: | |
| The schema to validate with | |
| cls (jsonschema.protocols.Validator): | |
| The class that will be used to validate the instance. | |
| If the ``cls`` argument is not provided, two things will happen | |
| in accordance with the specification. First, if the schema has a | |
| :kw:`$schema` keyword containing a known meta-schema [#]_ then the | |
| proper validator will be used. The specification recommends that | |
| all schemas contain :kw:`$schema` properties for this reason. If no | |
| :kw:`$schema` property is found, the default validator class is the | |
| latest released draft. | |
| Any other provided positional and keyword arguments will be passed | |
| on when instantiating the ``cls``. | |
| Raises: | |
| `jsonschema.exceptions.ValidationError`: | |
| if the instance is invalid | |
| `jsonschema.exceptions.SchemaError`: | |
| if the schema itself is invalid | |
| .. rubric:: Footnotes | |
| .. [#] known by a validator registered with | |
| `jsonschema.validators.validates` | |
| """ | |
| if cls is None: | |
| cls = validator_for(schema) | |
| cls.check_schema(schema) | |
| validator = cls(schema, *args, **kwargs) | |
| error = exceptions.best_match(validator.iter_errors(instance)) | |
| if error is not None: | |
| raise error | |
| def validator_for(schema, default=_UNSET) -> Validator: | |
| """ | |
| Retrieve the validator class appropriate for validating the given schema. | |
| Uses the :kw:`$schema` keyword that should be present in the given | |
| schema to look up the appropriate validator class. | |
| Arguments: | |
| schema (collections.abc.Mapping or bool): | |
| the schema to look at | |
| default: | |
| the default to return if the appropriate validator class | |
| cannot be determined. | |
| If unprovided, the default is to return the latest supported | |
| draft. | |
| Examples: | |
| The :kw:`$schema` JSON Schema keyword will control which validator | |
| class is returned: | |
| >>> schema = { | |
| ... "$schema": "https://json-schema.org/draft/2020-12/schema", | |
| ... "type": "integer", | |
| ... } | |
| >>> jsonschema.validators.validator_for(schema) | |
| <class 'jsonschema.validators.Draft202012Validator'> | |
| Here, a draft 7 schema instead will return the draft 7 validator: | |
| >>> schema = { | |
| ... "$schema": "http://json-schema.org/draft-07/schema#", | |
| ... "type": "integer", | |
| ... } | |
| >>> jsonschema.validators.validator_for(schema) | |
| <class 'jsonschema.validators.Draft7Validator'> | |
| Schemas with no ``$schema`` keyword will fallback to the default | |
| argument: | |
| >>> schema = {"type": "integer"} | |
| >>> jsonschema.validators.validator_for( | |
| ... schema, default=Draft7Validator, | |
| ... ) | |
| <class 'jsonschema.validators.Draft7Validator'> | |
| or if none is provided, to the latest version supported. | |
| Always including the keyword when authoring schemas is highly | |
| recommended. | |
| """ | |
| DefaultValidator = _LATEST_VERSION if default is _UNSET else default | |
| if schema is True or schema is False or "$schema" not in schema: | |
| return DefaultValidator | |
| if schema["$schema"] not in _META_SCHEMAS and default is _UNSET: | |
| warn( | |
| ( | |
| "The metaschema specified by $schema was not found. " | |
| "Using the latest draft to validate, but this will raise " | |
| "an error in the future." | |
| ), | |
| DeprecationWarning, | |
| stacklevel=2, | |
| ) | |
| return _META_SCHEMAS.get(schema["$schema"], DefaultValidator) | |