Skip to content

Commit 69fc7b4

Browse files
committed
Newer attrs API in validators.py and some type hints for create.
1 parent 8bdec06 commit 69fc7b4

File tree

3 files changed

+69
-29
lines changed

3 files changed

+69
-29
lines changed

jsonschema/_typing.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
Some (initially private) typing helpers for jsonschema's types.
3+
"""
4+
from typing import Any, Callable, Iterable, Protocol, Tuple, Union
5+
6+
import referencing.jsonschema
7+
8+
from jsonschema.protocols import Validator
9+
10+
11+
class SchemaKeywordValidator(Protocol):
12+
def __call__(
13+
self,
14+
validator: Validator,
15+
value: Any,
16+
instance: Any,
17+
schema: referencing.jsonschema.Schema,
18+
) -> None:
19+
...
20+
21+
22+
id_of = Callable[[referencing.jsonschema.Schema], Union[str, None]]
23+
24+
25+
ApplicableValidators = Callable[
26+
[referencing.jsonschema.Schema],
27+
Iterable[Tuple[str, Any]],
28+
]

jsonschema/protocols.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from __future__ import annotations
99

10-
from collections.abc import Callable, Mapping
10+
from collections.abc import Mapping
1111
from typing import TYPE_CHECKING, Any, ClassVar, Iterable
1212
import sys
1313

@@ -28,6 +28,7 @@
2828
# therefore, only import at type-checking time (to avoid circular references),
2929
# but use `jsonschema` for any types which will otherwise not be resolvable
3030
if TYPE_CHECKING:
31+
from jsonschema import _typing
3132
import jsonschema
3233
import jsonschema.validators
3334
import referencing.jsonschema
@@ -110,7 +111,7 @@ class Validator(Protocol):
110111
FORMAT_CHECKER: ClassVar[jsonschema.FormatChecker]
111112

112113
#: A function which given a schema returns its ID.
113-
ID_OF: Callable[[Any], str | None]
114+
ID_OF: _typing.id_of
114115

115116
#: The schema that will be used to validate instances
116117
schema: Mapping | bool

jsonschema/validators.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from __future__ import annotations
55

66
from collections import deque
7-
from collections.abc import Mapping, Sequence
7+
from collections.abc import Iterable, Mapping, Sequence
88
from functools import lru_cache
99
from operator import methodcaller
1010
from urllib.parse import unquote, urldefrag, urljoin, urlsplit
@@ -15,16 +15,17 @@
1515
import reprlib
1616
import warnings
1717

18+
from attrs import define, field, fields
1819
from jsonschema_specifications import REGISTRY as SPECIFICATIONS
1920
from referencing import Specification
2021
from rpds import HashTrieMap
21-
import attr
2222
import referencing.jsonschema
2323

2424
from jsonschema import (
2525
_format,
2626
_legacy_validators,
2727
_types,
28+
_typing,
2829
_utils,
2930
_validators,
3031
exceptions,
@@ -102,24 +103,29 @@ def _validates(cls):
102103

103104

104105
def create(
105-
meta_schema,
106-
validators=(),
107-
version=None,
108-
type_checker=_types.draft202012_type_checker,
109-
format_checker=_format.draft202012_format_checker,
110-
id_of=referencing.jsonschema.DRAFT202012.id_of,
111-
applicable_validators=methodcaller("items"),
106+
meta_schema: referencing.jsonschema.ObjectSchema,
107+
validators: (
108+
Mapping[str, _typing.SchemaKeywordValidator]
109+
| Iterable[tuple[str, _typing.SchemaKeywordValidator]]
110+
) = (),
111+
version: str | None = None,
112+
type_checker: _types.TypeChecker = _types.draft202012_type_checker,
113+
format_checker: _format.FormatChecker = _format.draft202012_format_checker,
114+
id_of: _typing.id_of = referencing.jsonschema.DRAFT202012.id_of,
115+
applicable_validators: _typing.ApplicableValidators = methodcaller(
116+
"items",
117+
),
112118
):
113119
"""
114120
Create a new validator class.
115121
116122
Arguments:
117123
118-
meta_schema (collections.abc.Mapping):
124+
meta_schema:
119125
120126
the meta schema for the new validator class
121127
122-
validators (collections.abc.Mapping):
128+
validators:
123129
124130
a mapping from names to callables, where each callable will
125131
validate the schema property with the given name.
@@ -132,37 +138,42 @@ def create(
132138
3. the instance
133139
4. the schema
134140
135-
version (str):
141+
version:
136142
137143
an identifier for the version that this validator class will
138144
validate. If provided, the returned validator class will
139145
have its ``__name__`` set to include the version, and also
140146
will have `jsonschema.validators.validates` automatically
141147
called for the given version.
142148
143-
type_checker (jsonschema.TypeChecker):
149+
type_checker:
144150
145151
a type checker, used when applying the :kw:`type` keyword.
146152
147153
If unprovided, a `jsonschema.TypeChecker` will be created
148154
with a set of default types typical of JSON Schema drafts.
149155
150-
format_checker (jsonschema.FormatChecker):
156+
format_checker:
151157
152158
a format checker, used when applying the :kw:`format` keyword.
153159
154160
If unprovided, a `jsonschema.FormatChecker` will be created
155161
with a set of default formats typical of JSON Schema drafts.
156162
157-
id_of (collections.abc.Callable):
163+
id_of:
158164
159165
A function that given a schema, returns its ID.
160166
161-
applicable_validators (collections.abc.Callable):
167+
applicable_validators:
162168
163-
A function that given a schema, returns the list of
164-
applicable validators (validation keywords and callables)
169+
A function that, given a schema, returns the list of
170+
applicable schema keywords and associated values
165171
which will be used to validate the instance.
172+
This is mostly used to support pre-draft 7 versions of JSON Schema
173+
which specified behavior around ignoring keywords if they were
174+
siblings of a ``$ref`` keyword. If you're not attempting to
175+
implement similar behavior, you can typically ignore this argument
176+
and leave it at its default.
166177
167178
Returns:
168179
@@ -176,7 +187,7 @@ def create(
176187
default=Specification.OPAQUE,
177188
)
178189

179-
@attr.s
190+
@define
180191
class Validator:
181192

182193
VALIDATORS = dict(validators)
@@ -185,17 +196,17 @@ class Validator:
185196
FORMAT_CHECKER = format_checker_arg
186197
ID_OF = staticmethod(id_of)
187198

188-
schema = attr.ib(repr=reprlib.repr)
189-
_ref_resolver = attr.ib(default=None, repr=False, alias="resolver")
190-
format_checker = attr.ib(default=None)
199+
schema: referencing.jsonschema.Schema = field(repr=reprlib.repr)
200+
_ref_resolver = field(default=None, repr=False, alias="resolver")
201+
format_checker: _format.FormatChecker | None = field(default=None)
191202
# TODO: include new meta-schemas added at runtime
192-
_registry = attr.ib(
203+
_registry: referencing.jsonschema.SchemaRegistry = field(
193204
default=SPECIFICATIONS,
194205
converter=SPECIFICATIONS.combine, # type: ignore[misc]
195206
kw_only=True,
196207
repr=False,
197208
)
198-
_resolver = attr.ib(
209+
_resolver = field(
199210
alias="_resolver",
200211
default=None,
201212
kw_only=True,
@@ -222,7 +233,7 @@ def evolve(self, **changes):
222233
schema = changes.setdefault("schema", self.schema)
223234
NewValidator = validator_for(schema, default=cls)
224235

225-
for field in attr.fields(cls):
236+
for field in fields(cls): # noqa: F402
226237
if not field.init:
227238
continue
228239
attr_name = field.name
@@ -429,14 +440,14 @@ def is_valid(self, instance, _schema=None):
429440

430441
evolve_fields = [
431442
(field.name, field.alias)
432-
for field in attr.fields(Validator)
443+
for field in fields(Validator)
433444
if field.init
434445
]
435446

436447
if version is not None:
437448
safe = version.title().replace(" ", "").replace("-", "")
438449
Validator.__name__ = Validator.__qualname__ = f"{safe}Validator"
439-
Validator = validates(version)(Validator)
450+
Validator = validates(version)(Validator) # type: ignore[misc]
440451

441452
return Validator
442453

0 commit comments

Comments
 (0)