From a60a087884e63a6673bebbd403588958c5b047b3 Mon Sep 17 00:00:00 2001 From: Julian Berman Date: Fri, 19 Aug 2022 13:27:22 +0300 Subject: [PATCH] Support validator classes whose metaschema uses a different dialect. In other words, one may author validator classes (via jsonschema.validators.create or extend) whose meta schema defines schema behavior using JSON Schema draft2020-12 but whose schemas are in its own dialect. Said again differently, the set of valid schemas for a validator class may be governed by one draft, while the schema behavior itself is governed by another. --- CHANGELOG.rst | 10 +++++++++ jsonschema/tests/test_validators.py | 32 +++++++++++++++++++++++++++++ jsonschema/validators.py | 3 ++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eb97d9dc2..1daec673d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,13 @@ +v4.13.0 +======= + +* Add support for creating validator classes whose metaschema uses a different + dialect than its schemas. In other words, they may use draft2020-12 to define + which schemas are valid, but the schemas themselves use draft7 (or a custom + dialect, etc.) to define which *instances* are valid. Doing this is likely + not something most users, even metaschema authors, may need, but occasionally + will be useful for advanced use cases. + v4.12.1 ======= diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index d49dba0a9..ea7d3e476 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -197,6 +197,38 @@ def test_create_default_types(self): ), ) + def test_check_schema_with_different_metaschema(self): + """ + One can create a validator class whose metaschema uses a different + dialect than itself. + """ + + NoEmptySchemasValidator = validators.create( + meta_schema={ + "$schema": validators.Draft202012Validator.META_SCHEMA["$id"], + "not": {"const": {}}, + }, + ) + NoEmptySchemasValidator.check_schema({"foo": "bar"}) + + with self.assertRaises(exceptions.SchemaError): + NoEmptySchemasValidator.check_schema({}) + + NoEmptySchemasValidator({"foo": "bar"}).validate("foo") + + def test_check_schema_with_different_metaschema_defaults_to_self(self): + """ + A validator whose metaschema doesn't declare $schema defaults to its + own validation behavior, not the latest "normal" specification. + """ + + NoEmptySchemasValidator = validators.create( + meta_schema={"fail": [{"message": "Meta schema whoops!"}]}, + validators={"fail": fail}, + ) + with self.assertRaises(exceptions.SchemaError): + NoEmptySchemasValidator.check_schema({}) + def test_extend(self): original = dict(self.Validator.VALIDATORS) new = object() diff --git a/jsonschema/validators.py b/jsonschema/validators.py index abdcb4c43..39e29904f 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -215,7 +215,8 @@ def __attrs_post_init__(self): @classmethod def check_schema(cls, schema): - for error in cls(cls.META_SCHEMA).iter_errors(schema): + Validator = validator_for(cls.META_SCHEMA, default=cls) + for error in Validator(cls.META_SCHEMA).iter_errors(schema): raise exceptions.SchemaError.create_from(error) def evolve(self, **changes):