protocols.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. """
  2. typing.Protocol classes for jsonschema interfaces.
  3. """
  4. # for reference material on Protocols, see
  5. # https://www.python.org/dev/peps/pep-0544/
  6. from __future__ import annotations
  7. from typing import (
  8. TYPE_CHECKING,
  9. Any,
  10. ClassVar,
  11. Iterable,
  12. Protocol,
  13. runtime_checkable,
  14. )
  15. # in order for Sphinx to resolve references accurately from type annotations,
  16. # it needs to see names like `jsonschema.TypeChecker`
  17. # therefore, only import at type-checking time (to avoid circular references),
  18. # but use `jsonschema` for any types which will otherwise not be resolvable
  19. if TYPE_CHECKING:
  20. from collections.abc import Mapping
  21. import referencing.jsonschema
  22. from jsonschema import _typing
  23. from jsonschema.exceptions import ValidationError
  24. import jsonschema
  25. import jsonschema.validators
  26. # For code authors working on the validator protocol, these are the three
  27. # use-cases which should be kept in mind:
  28. #
  29. # 1. As a protocol class, it can be used in type annotations to describe the
  30. # available methods and attributes of a validator
  31. # 2. It is the source of autodoc for the validator documentation
  32. # 3. It is runtime_checkable, meaning that it can be used in isinstance()
  33. # checks.
  34. #
  35. # Since protocols are not base classes, isinstance() checking is limited in
  36. # its capabilities. See docs on runtime_checkable for detail
  37. @runtime_checkable
  38. class Validator(Protocol):
  39. """
  40. The protocol to which all validator classes adhere.
  41. Arguments:
  42. schema:
  43. The schema that the validator object will validate with.
  44. It is assumed to be valid, and providing
  45. an invalid schema can lead to undefined behavior. See
  46. `Validator.check_schema` to validate a schema first.
  47. registry:
  48. a schema registry that will be used for looking up JSON references
  49. resolver:
  50. a resolver that will be used to resolve :kw:`$ref`
  51. properties (JSON references). If unprovided, one will be created.
  52. .. deprecated:: v4.18.0
  53. `RefResolver <_RefResolver>` has been deprecated in favor of
  54. `referencing`, and with it, this argument.
  55. format_checker:
  56. if provided, a checker which will be used to assert about
  57. :kw:`format` properties present in the schema. If unprovided,
  58. *no* format validation is done, and the presence of format
  59. within schemas is strictly informational. Certain formats
  60. require additional packages to be installed in order to assert
  61. against instances. Ensure you've installed `jsonschema` with
  62. its `extra (optional) dependencies <index:extras>` when
  63. invoking ``pip``.
  64. .. deprecated:: v4.12.0
  65. Subclassing validator classes now explicitly warns this is not part of
  66. their public API.
  67. """
  68. #: An object representing the validator's meta schema (the schema that
  69. #: describes valid schemas in the given version).
  70. META_SCHEMA: ClassVar[Mapping]
  71. #: A mapping of validation keywords (`str`\s) to functions that
  72. #: validate the keyword with that name. For more information see
  73. #: `creating-validators`.
  74. VALIDATORS: ClassVar[Mapping]
  75. #: A `jsonschema.TypeChecker` that will be used when validating
  76. #: :kw:`type` keywords in JSON schemas.
  77. TYPE_CHECKER: ClassVar[jsonschema.TypeChecker]
  78. #: A `jsonschema.FormatChecker` that will be used when validating
  79. #: :kw:`format` keywords in JSON schemas.
  80. FORMAT_CHECKER: ClassVar[jsonschema.FormatChecker]
  81. #: A function which given a schema returns its ID.
  82. ID_OF: _typing.id_of
  83. #: The schema that will be used to validate instances
  84. schema: Mapping | bool
  85. def __init__(
  86. self,
  87. schema: Mapping | bool,
  88. registry: referencing.jsonschema.SchemaRegistry,
  89. format_checker: jsonschema.FormatChecker | None = None,
  90. ) -> None:
  91. ...
  92. @classmethod
  93. def check_schema(cls, schema: Mapping | bool) -> None:
  94. """
  95. Validate the given schema against the validator's `META_SCHEMA`.
  96. Raises:
  97. `jsonschema.exceptions.SchemaError`:
  98. if the schema is invalid
  99. """
  100. def is_type(self, instance: Any, type: str) -> bool:
  101. """
  102. Check if the instance is of the given (JSON Schema) type.
  103. Arguments:
  104. instance:
  105. the value to check
  106. type:
  107. the name of a known (JSON Schema) type
  108. Returns:
  109. whether the instance is of the given type
  110. Raises:
  111. `jsonschema.exceptions.UnknownType`:
  112. if ``type`` is not a known type
  113. """
  114. def is_valid(self, instance: Any) -> bool:
  115. """
  116. Check if the instance is valid under the current `schema`.
  117. Returns:
  118. whether the instance is valid or not
  119. >>> schema = {"maxItems" : 2}
  120. >>> Draft202012Validator(schema).is_valid([2, 3, 4])
  121. False
  122. """
  123. def iter_errors(self, instance: Any) -> Iterable[ValidationError]:
  124. r"""
  125. Lazily yield each of the validation errors in the given instance.
  126. >>> schema = {
  127. ... "type" : "array",
  128. ... "items" : {"enum" : [1, 2, 3]},
  129. ... "maxItems" : 2,
  130. ... }
  131. >>> v = Draft202012Validator(schema)
  132. >>> for error in sorted(v.iter_errors([2, 3, 4]), key=str):
  133. ... print(error.message)
  134. 4 is not one of [1, 2, 3]
  135. [2, 3, 4] is too long
  136. .. deprecated:: v4.0.0
  137. Calling this function with a second schema argument is deprecated.
  138. Use `Validator.evolve` instead.
  139. """
  140. def validate(self, instance: Any) -> None:
  141. """
  142. Check if the instance is valid under the current `schema`.
  143. Raises:
  144. `jsonschema.exceptions.ValidationError`:
  145. if the instance is invalid
  146. >>> schema = {"maxItems" : 2}
  147. >>> Draft202012Validator(schema).validate([2, 3, 4])
  148. Traceback (most recent call last):
  149. ...
  150. ValidationError: [2, 3, 4] is too long
  151. """
  152. def evolve(self, **kwargs) -> Validator:
  153. """
  154. Create a new validator like this one, but with given changes.
  155. Preserves all other attributes, so can be used to e.g. create a
  156. validator with a different schema but with the same :kw:`$ref`
  157. resolution behavior.
  158. >>> validator = Draft202012Validator({})
  159. >>> validator.evolve(schema={"type": "number"})
  160. Draft202012Validator(schema={'type': 'number'}, format_checker=None)
  161. The returned object satisfies the validator protocol, but may not
  162. be of the same concrete class! In particular this occurs
  163. when a :kw:`$ref` occurs to a schema with a different
  164. :kw:`$schema` than this one (i.e. for a different draft).
  165. >>> validator.evolve(
  166. ... schema={"$schema": Draft7Validator.META_SCHEMA["$id"]}
  167. ... )
  168. Draft7Validator(schema=..., format_checker=None)
  169. """