diff --git a/jsonschema/_keywords.py b/jsonschema/_keywords.py index 45b53c9c..56f59f42 100644 --- a/jsonschema/_keywords.py +++ b/jsonschema/_keywords.py @@ -447,3 +447,8 @@ def prefixItems(validator, prefixItems, instance, schema): schema_path=index, path=index, ) + + +def deprecated(validator, deprecated, instance, schema): + if validator.check_deprecated and deprecated is True: + yield ValidationError(f"{instance!r} is deprecated") diff --git a/jsonschema/protocols.py b/jsonschema/protocols.py index 4ad43e70..d032bd1f 100644 --- a/jsonschema/protocols.py +++ b/jsonschema/protocols.py @@ -81,6 +81,12 @@ class Validator(Protocol): its `extra (optional) dependencies ` when invoking ``pip``. + check_deprecated: + + if ``True``, raise validation errors when the :kw:`deprecated` + keyword is ``true`` for an instance value, indicating it should not + be used and may be removed in the future. + .. deprecated:: v4.12.0 Subclassing validator classes now explicitly warns this is not part of @@ -216,7 +222,7 @@ def evolve(self, **kwargs) -> Validator: >>> validator = Draft202012Validator({}) >>> validator.evolve(schema={"type": "number"}) - Draft202012Validator(schema={'type': 'number'}, format_checker=None) + Draft202012Validator(schema={'type': 'number'}, format_checker=None, check_deprecated=False) The returned object satisfies the validator protocol, but may not be of the same concrete class! In particular this occurs @@ -226,5 +232,5 @@ def evolve(self, **kwargs) -> Validator: >>> validator.evolve( ... schema={"$schema": Draft7Validator.META_SCHEMA["$id"]} ... ) - Draft7Validator(schema=..., format_checker=None) - """ + Draft7Validator(schema=..., format_checker=None, check_deprecated=False) + """ # noqa: E501 diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index a15c8ff7..53e6d8e2 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -127,7 +127,8 @@ def test_repr(self): self.addCleanup(validators._VALIDATORS.pop, "my version") self.assertEqual( repr(Validator({})), - "MyVersionValidator(schema={}, format_checker=None)", + "MyVersionValidator(schema={}, format_checker=None, " + "check_deprecated=False)", ) def test_long_repr(self): @@ -140,7 +141,7 @@ def test_long_repr(self): self.assertEqual( repr(Validator({"a": list(range(1000))})), ( "MyVersionValidator(schema={'a': [0, 1, 2, 3, 4, 5, ...]}, " - "format_checker=None)" + "format_checker=None, check_deprecated=False)" ), ) @@ -148,7 +149,8 @@ def test_repr_no_version(self): Validator = validators.create(meta_schema={}) self.assertEqual( repr(Validator({})), - "Validator(schema={}, format_checker=None)", + "Validator(schema={}, format_checker=None, " + "check_deprecated=False)", ) def test_dashes_are_stripped_from_validator_names(self): @@ -530,6 +532,32 @@ def test_maxLength(self): ) self.assertEqual(message, "'abc' is too long") + def test_deprecated(self): + message = self.message_for( + instance={"id": 1, "name": "abc"}, + schema={ + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string", "deprecated": True}, + }, + }, + check_deprecated=True, + ) + self.assertEqual(message, "'abc' is deprecated") + + def test_deprecated_disabled(self): + schema = { + "properties": { + "id": {"type": "integer"}, + "name": {"type": "string", "deprecated": True}, + }, + } + instance={"id": 1, "name": "abc"} + + validator = validators.Draft202012Validator(schema) + errors = list(validator.iter_errors(instance)) + self.assertListEqual(errors, []) + def test_pattern(self): message = self.message_for( instance="bbb", diff --git a/jsonschema/validators.py b/jsonschema/validators.py index 3acee870..a7ad5487 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -229,6 +229,7 @@ class Validator: schema: referencing.jsonschema.Schema = field(repr=reprlib.repr) _ref_resolver = field(default=None, repr=False, alias="resolver") format_checker: _format.FormatChecker | None = field(default=None) + check_deprecated: bool = field(default=False) # TODO: include new meta-schemas added at runtime _registry: referencing.jsonschema.SchemaRegistry = field( default=_REMOTE_WARNING_REGISTRY, @@ -758,6 +759,7 @@ def extend( "contains": _keywords.contains, "dependentRequired": _keywords.dependentRequired, "dependentSchemas": _keywords.dependentSchemas, + "deprecated": _keywords.deprecated, "enum": _keywords.enum, "exclusiveMaximum": _keywords.exclusiveMaximum, "exclusiveMinimum": _keywords.exclusiveMinimum, @@ -805,6 +807,7 @@ def extend( "contains": _keywords.contains, "dependentRequired": _keywords.dependentRequired, "dependentSchemas": _keywords.dependentSchemas, + "deprecated": _keywords.deprecated, "enum": _keywords.enum, "exclusiveMaximum": _keywords.exclusiveMaximum, "exclusiveMinimum": _keywords.exclusiveMinimum,