diff options
author | Julian Berman <Julian+git@GrayVines.com> | 2012-01-15 20:42:51 -0500 |
---|---|---|
committer | Julian Berman <Julian+git@GrayVines.com> | 2012-01-15 20:42:51 -0500 |
commit | ce63985a3d854c016985923d55e8716d5aa579c5 (patch) | |
tree | 82cd40d51a610383703f00b07ca70fd0f2292bd2 | |
parent | 0500c5069db7e997bb71cdab497e6b8e8a17d5b2 (diff) | |
download | jsonschema-ce63985a3d854c016985923d55e8716d5aa579c5.tar.gz |
Make {max,min}{Items,Length} stricter, and 'clean up' type checking.
-rw-r--r-- | jsonschema.py | 86 | ||||
-rw-r--r-- | tests.py | 5 |
2 files changed, 58 insertions, 33 deletions
diff --git a/jsonschema.py b/jsonschema.py index 2c3b3db..014bb6b 100644 --- a/jsonschema.py +++ b/jsonschema.py @@ -113,6 +113,35 @@ class Validator(object): ) self._types[u"any"] = tuple(self._types.values()) + def _is_type(self, instance, type): + """ + Check if an ``instance`` is of the provided ``type``. + + """ + + py_type = self._types.get(type) + + if py_type is None: + return self._schema_error( + self._unknown_type, u"%r is not a known type" % (type,) + ) + + # the only thing we're careful about here is evading bool inheriting + # from int, so let's be even dirtier than usual + + elif ( + # it's not a bool, so no worries + not isinstance(instance, bool) or + + # it is a bool, but we're checking for a bool, so no worries + ( + py_type is bool or + isinstance(py_type, tuple) and bool in py_type + ) + + ): + return isinstance(instance, py_type) + def _error(self, msg): """ Something failed to validate. ``msg`` will have details. @@ -191,31 +220,22 @@ class Validator(object): types = _list(types) for type in types: - if ( - isinstance(type, dict) and - isinstance(instance, dict) and + # Ouch. Brain hurts. Two paths here, either we have a schema, then + # check if the instance is valid under it + if (( + self._is_type(type, "object") and + self._is_type(instance, "object") and self.is_valid(instance, type) - ): - return - - elif isinstance(type, basestring): - py_type = self._types.get(type) - if py_type is None: - self._schema_error( - self._unknown_type, u"%r is not a known type" % (type,) - ) - return + # 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 + # something other than error. If so, bail out. - # isinstance(a_bool, int) will make us even sadder here, so - # let's be even dirtier than we would otherwise be. - - elif ( - isinstance(instance, py_type) and - (not isinstance(instance, bool) or - py_type is bool or types == [u"any"]) - ): - return + ) or ( + self._is_type(type, "string") and + (self._is_type(instance, type) or type not in self._types) + )): + return else: self._error(u"%r is not of type %s" % (instance, _delist(types))) @@ -223,7 +243,7 @@ class Validator(object): for property, subschema in properties.iteritems(): if property in instance: dependencies = _list(subschema.get(u"dependencies", [])) - if isinstance(dependencies, dict): + if self._is_type(dependencies, "object"): self._validate(instance, dependencies) else: missing = (d for d in dependencies if d not in instance) @@ -247,14 +267,14 @@ class Validator(object): # no viewkeys in <2.7, and pypy seems to fail on vk - vk anyhow, so... extras = set(instance) - set(schema.get(u"properties", {})) - if isinstance(aP, dict): + if self._is_type(aP, "object"): for extra in extras: self._validate(instance[extra], aP) elif not aP and extras: self._error(u"Additional properties are not allowed") def validate_items(self, items, instance, schema): - if isinstance(items, dict): + if self._is_type(items, "object"): for item in instance: self._validate(item, items) else: @@ -262,7 +282,7 @@ class Validator(object): self._validate(item, subschema) def validate_additionalItems(self, aI, instance, schema): - if isinstance(aI, dict): + if self._is_type(aI, "object"): for item in instance[len(schema):]: self._validate(item, aI) elif not aI and len(instance) > len(schema): @@ -295,23 +315,23 @@ class Validator(object): ) def validate_minItems(self, mI, instance, schema): - if len(instance) < mI: + if self._is_type(instance, "array") and len(instance) < mI: self._error(u"%r is too short" % (instance,)) def validate_maxItems(self, mI, instance, schema): - if len(instance) > mI: + if self._is_type(instance, "array") and len(instance) > mI: self._error(u"%r is too long" % (instance,)) - def validate_pattern(self, pattern, instance, schema): - if not re.match(pattern, instance): - self._error(u"%r does not match %r" % (instance, pattern)) + def validate_pattern(self, patrn, instance, schema): + if self._is_type(instance, "string") and not re.match(patrn, instance): + self._error(u"%r does not match %r" % (instance, patrn)) def validate_minLength(self, mL, instance, schema): - if len(instance) < mL: + if self._is_type(instance, "string") and len(instance) < mL: self._error(u"%r is too short" % (instance,)) def validate_maxLength(self, mL, instance, schema): - if len(instance) > mL: + if self._is_type(instance, "string") and len(instance) > mL: self._error(u"%r is too long" % (instance,)) def validate_enum(self, enums, instance, schema): @@ -360,6 +360,7 @@ class TestValidate(unittest.TestCase): ("exact", "valid", [1]), ("longer", "valid", [1, 2]), ("too_short", "invalid", []), + ("ignores_strings", "valid", "a"), )(validation_test(minItems=1)) maxItems = parametrized( @@ -367,21 +368,25 @@ class TestValidate(unittest.TestCase): ("shorter", "valid", [1]), ("empty", "valid", []), ("too_long", "invalid", [1, 2, 3]), + ("ignores_strings", "valid", "aaaa"), )(validation_test(maxItems=2)) pattern = parametrized( ("match", "valid", u"aaa"), ("mismatch", "invalid", u"ab"), + ("ignores_other_stuff", "valid", True), )(validation_test(pattern=u"^a*$")) minLength = parametrized( ("", "valid", u"foo"), ("too_short", "invalid", u"f"), + ("ignores_arrays", "valid", [1]), )(validation_test(minLength=2)) maxLength = parametrized( ("", "valid", u"f"), ("too_long", "invalid", u"foo"), + ("ignores_arrays", "valid", [1, 2, 3]), )(validation_test(maxLength=2)) @parametrized( |