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