diff --git a/jsonschema/_validators.py b/jsonschema/_validators.py index cfdf66a..d0baec8 100644 --- a/jsonschema/_validators.py +++ b/jsonschema/_validators.py @@ -29,8 +29,21 @@ def additionalProperties(validator, aP, instance, schema): for error in validator.descend(instance[extra], aP, path=extra): yield error elif not aP and extras: - error = "Additional properties are not allowed (%s %s unexpected)" - yield ValidationError(error % _utils.extras_msg(extras)) + if "patternProperties" in schema: + patterns = sorted(schema["patternProperties"]) + if len(extras) == 1: + verb = "does" + else: + verb = "do" + error = "%s %s not match any of the regexes: %s" % ( + ", ".join(map(repr, sorted(extras))), + verb, + ", ".join(map(repr, patterns)), + ) + yield ValidationError(error) + else: + error = "Additional properties are not allowed (%s %s unexpected)" + yield ValidationError(error % _utils.extras_msg(extras)) def items(validator, items, instance, schema): diff --git a/jsonschema/tests/test_validators.py b/jsonschema/tests/test_validators.py index 9a9d1cf..fb59b83 100644 --- a/jsonschema/tests/test_validators.py +++ b/jsonschema/tests/test_validators.py @@ -191,6 +191,30 @@ class TestValidationErrorMessages(unittest.TestCase): self.assertIn(repr("thing"), message) self.assertIn("is not a", message) + def test_additionalProperties_false_patternProperties(self): + schema = {u"type": u"object", + u"additionalProperties": False, + u"patternProperties": { + u"^abc$": {u"type": u"string"}, + u"^def$": {u"type": u"string"} + }} + message = self.message_for({u"zebra": 123}, schema, + cls=Draft4Validator) + self.assertEqual( + message, + "{} does not match any of the regexes: {}, {}".format( + repr(u"zebra"), repr(u"^abc$"), repr(u"^def$"), + ), + ) + message = self.message_for({u"zebra": 123, u"fish": 456}, schema, + cls=Draft4Validator) + self.assertEqual( + message, + "{}, {} do not match any of the regexes: {}, {}".format( + repr(u"fish"), repr(u"zebra"), repr(u"^abc$"), repr(u"^def$") + ), + ) + class TestValidationErrorDetails(unittest.TestCase): # TODO: These really need unit tests for each individual validator, rather