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