Skip to content

Commit

Permalink
Add a hack to access root schema from $ref validator.
Browse files Browse the repository at this point in the history
Add unit tests for $ref as json-pointer.
refs python-jsonschema#23
  • Loading branch information
gazpachoking committed Sep 23, 2012
1 parent 4f6bbe6 commit b3979bf
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 16 deletions.
46 changes: 30 additions & 16 deletions jsonschema.py
Expand Up @@ -237,6 +237,7 @@ def __init__(
self._types = dict(self.DEFAULT_TYPES)
self._types.update(types)
self._types["any"] = tuple(self._types.values())
self._root_schema = None

def is_type(self, instance, type):
"""
Expand Down Expand Up @@ -310,19 +311,28 @@ def iter_errors(self, instance, schema, meta_validate=True):
# I think we're safer raising these always, not yielding them
raise s

for k, v in iteritems(schema):
validator = getattr(self, "validate_%s" % (k.lstrip("$"),), None)

if validator is None:
errors = self.unknown_property(k, instance, schema)
else:
errors = validator(v, instance, schema)

for error in errors or ():
# if the validator hasn't already been set (due to recursion)
# make sure to set it
error.validator = error.validator or k
yield error
# Hack to be able to access root schema from $ref validator
is_root_schema = False
if self._root_schema is None:
self._root_schema = schema
is_root_schema = True
try:
for k, v in iteritems(schema):
validator = getattr(self, "validate_%s" % (k.lstrip("$"),), None)

if validator is None:
errors = self.unknown_property(k, instance, schema)
else:
errors = validator(v, instance, schema)

for error in errors or ():
# if the validator hasn't already been set (due to recursion)
# make sure to set it
error.validator = error.validator or k
yield error
finally:
if is_root_schema:
self._root_schema = None

def validate(self, *args, **kwargs):
"""
Expand All @@ -347,7 +357,7 @@ def validate_type(self, types, instance, schema):
# check if the instance is valid under it
if ((
self.is_type(type, "object") and
self.is_valid(instance, type)
self.is_valid(instance, type, meta_validate=False)

# Or we have a type as a string, just check if the instance is that
# type. Also, HACK: we can reach the `or` here if skip_types is
Expand Down Expand Up @@ -409,6 +419,8 @@ def validate_additionalProperties(self, aP, instance, schema):
yield ValidationError(error % _extras_msg(extras))

def validate_dependencies(self, dependencies, instance, schema):
if not self.is_type(instance, "object"):
return

This comment has been minimized.

Copy link
@gazpachoking

gazpachoking Sep 23, 2012

Author Owner

I think this is a bug fix for dependencies implementation. My reading of the spec means that "dependencies" property should be ignored when instance is not an object.

for property, dependency in iteritems(dependencies):
if property not in instance:
continue
Expand Down Expand Up @@ -536,7 +548,9 @@ def validate_divisibleBy(self, dB, instance, schema):

def validate_disallow(self, disallow, instance, schema):
for disallowed in _list(disallow):
if self.is_valid(instance, {"type" : [disallowed]}):
if self.is_valid(
instance, {"type" : [disallowed]}, meta_validate=False
):
yield ValidationError(
"%r is disallowed for %r" % (disallowed, instance)
)
Expand All @@ -560,7 +574,7 @@ def validate_ref(self, ref, instance, schema):
parts = map(unquote, parts)
parts = [part.replace('~1', '/').replace('~0', '~')
for part in parts]
pointer = schema # TODO: Ahh, how do we get the root schema here?
pointer = self._root_schema
try:
for part in parts:
pointer = pointer[part]
Expand Down
17 changes: 17 additions & 0 deletions tests.py
Expand Up @@ -669,6 +669,23 @@ def test_iter_errors_multiple_failures_one_validator(self):
errors = list(Validator().iter_errors(instance, schema))
self.assertEqual(len(errors), 4)

root_pointer_ref = parametrized(
("match", "valid", {"foo": False}),
("recursive_match", "valid", {"foo": {"foo": False}}),
("mismatch", "invalid", {"bar": False}),
("recursive_mismatch", "invalid", {"foo": {"bar": False}})
)(validation_test(
properties={"foo": {"$ref": "#"}}, additionalProperties=False)
)

relative_pointer_ref = parametrized(
("match", "valid", {"bar": 3}),
("mismatch", "invalid", {"bar": True})
)(validation_test(
properties={"foo": {"type": "integer"},
"bar": {"$ref": "#/properties/foo"}}
))


class TestValidationErrorDetails(unittest.TestCase):
# TODO: These really need unit tests for each individual validator, rather
Expand Down

0 comments on commit b3979bf

Please sign in to comment.