Skip to content

Commit 18a64d7

Browse files
committed
Add support for referencing schemas across different drafts.
In other words, a draft 7 schema may reference a draft 2019 schema (or vice versa), and now correctly apply the 2019 rules within the referenced schema. Refs: json-schema-org/JSON-Schema-Test-Suite#587
1 parent 9aea572 commit 18a64d7

File tree

4 files changed

+35
-1
lines changed

4 files changed

+35
-1
lines changed

CHANGELOG.rst

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
v4.10.0
2+
-------
3+
4+
* Add support for referencing schemas with ``$ref`` across different versions
5+
of the specification than the referrer's
6+
17
v4.9.1
28
------
39

jsonschema/protocols.py

+10
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,14 @@ def evolve(self, **kwargs) -> "Validator":
168168
>>> validator = Draft202012Validator({})
169169
>>> validator.evolve(schema={"type": "number"})
170170
Draft202012Validator(schema={'type': 'number'}, format_checker=None)
171+
172+
The returned object satisfies the validator protocol, but may not
173+
be of the same concrete class! In particular this occurs
174+
when a :validator:`$ref` occurs to a schema with a different
175+
:validator:`$schema` than this one (i.e. for a different draft).
176+
177+
>>> validator.evolve(
178+
... schema={"$schema": Draft7Validator.META_SCHEMA["$id"]}
179+
... )
180+
Draft7Validator(schema=..., format_checker=None)
171181
"""

jsonschema/tests/test_jsonschema_test_suite.py

+3
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ def leap_second(test):
264264
DRAFT7.format_tests(),
265265
DRAFT7.optional_tests_of(name="bignum"),
266266
DRAFT7.optional_tests_of(name="content"),
267+
DRAFT7.optional_tests_of(name="cross-draft"),
267268
DRAFT7.optional_tests_of(name="float-overflow"),
268269
DRAFT7.optional_tests_of(name="non-bmp-regex"),
269270
Validator=jsonschema.Draft7Validator,
@@ -323,6 +324,7 @@ def leap_second(test):
323324
TestDraft201909 = DRAFT201909.to_unittest_testcase(
324325
DRAFT201909.tests(),
325326
DRAFT201909.optional_tests_of(name="bignum"),
327+
DRAFT201909.optional_tests_of(name="cross-draft"),
326328
DRAFT201909.optional_tests_of(name="float-overflow"),
327329
DRAFT201909.optional_tests_of(name="non-bmp-regex"),
328330
DRAFT201909.optional_tests_of(name="refOfUnknownKeyword"),
@@ -507,6 +509,7 @@ def leap_second(test):
507509
TestDraft202012 = DRAFT202012.to_unittest_testcase(
508510
DRAFT202012.tests(),
509511
DRAFT202012.optional_tests_of(name="bignum"),
512+
DRAFT202012.optional_tests_of(name="cross-draft"),
510513
DRAFT202012.optional_tests_of(name="float-overflow"),
511514
DRAFT202012.optional_tests_of(name="non-bmp-regex"),
512515
DRAFT202012.optional_tests_of(name="refOfUnknownKeyword"),

jsonschema/validators.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ class Validator:
189189
schema = attr.ib(repr=reprlib.repr)
190190
resolver = attr.ib(default=None, repr=False)
191191
format_checker = attr.ib(default=None)
192-
evolve = attr.evolve
193192

194193
def __attrs_post_init__(self):
195194
if self.resolver is None:
@@ -203,6 +202,22 @@ def check_schema(cls, schema):
203202
for error in cls(cls.META_SCHEMA).iter_errors(schema):
204203
raise exceptions.SchemaError.create_from(error)
205204

205+
def evolve(self, **changes):
206+
schema = changes.setdefault("schema", self.schema)
207+
NewValidator = validator_for(schema, default=Validator)
208+
209+
# Essentially reproduces attr.evolve, but may involve instantiating
210+
# a different class than this one.
211+
for field in attr.fields(Validator):
212+
if not field.init:
213+
continue
214+
attr_name = field.name # To deal with private attributes.
215+
init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
216+
if init_name not in changes:
217+
changes[init_name] = getattr(self, attr_name)
218+
219+
return NewValidator(**changes)
220+
206221
def iter_errors(self, instance, _schema=None):
207222
if _schema is not None:
208223
warnings.warn(

0 commit comments

Comments
 (0)