diff --git a/jsonschema/_validators.py b/jsonschema/_validators.py index 874e8796..8542a879 100644 --- a/jsonschema/_validators.py +++ b/jsonschema/_validators.py @@ -435,23 +435,32 @@ def unevaluatedItems(validator, unevaluatedItems, instance, schema): def unevaluatedProperties(validator, unevaluatedProperties, instance, schema): if not validator.is_type(instance, "object"): return - evaluated_property_keys = find_evaluated_property_keys_by_schema( + evaluated_keys = find_evaluated_property_keys_by_schema( validator, instance, schema, ) - unevaluated_property_keys = [] + unevaluated_keys = [] for property in instance: - if property not in evaluated_property_keys: + if property not in evaluated_keys: for _ in validator.descend( instance[property], unevaluatedProperties, path=property, schema_path=property, ): - unevaluated_property_keys.append(property) - - if unevaluated_property_keys: - error = "Unevaluated properties are not allowed (%s %s unexpected)" - yield ValidationError(error % extras_msg(unevaluated_property_keys)) + # FIXME: Include context for each unevaluated property + # indicating why it's invalid under the subschema. + unevaluated_keys.append(property) + + if unevaluated_keys: + if unevaluatedProperties is False: + error = "Unevaluated properties are not allowed (%s %s unexpected)" + yield ValidationError(error % extras_msg(unevaluated_keys)) + else: + error = ( + "Unevaluated properties are not valid under " + "the given schema (%s %s unevaluated and invalid)" + ) + yield ValidationError(error % extras_msg(unevaluated_keys)) def prefixItems(validator, prefixItems, instance, schema): diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 12c9dc9d..8f8680a0 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -634,7 +634,26 @@ def test_unevaluated_items_on_invalid_type(self): message = self.message_for(instance="foo", schema=schema) self.assertEqual(message, "'foo' is not of type 'array'") - def test_unevaluated_properties(self): + def test_unevaluated_properties_invalid_against_subschema(self): + schema = { + "properties": {"foo": {"type": "string"}}, + "unevaluatedProperties": {"const": 12}, + } + message = self.message_for( + instance={ + "foo": "foo", + "bar": "bar", + "baz": 12, + }, + schema=schema, + ) + self.assertEqual( + message, + "Unevaluated properties are not valid under the given schema " + "('bar' was unevaluated and invalid)", + ) + + def test_unevaluated_properties_disallowed(self): schema = {"type": "object", "unevaluatedProperties": False} message = self.message_for( instance={