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