PK5¼FH™nŒ2æRæR"json_schema_validator/validator.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """Validator implementation.""" import re import datetime import itertools import types import sys from json_schema_validator.errors import ValidationError from json_schema_validator.misc import NUMERIC_TYPES from json_schema_validator.schema import Schema if sys.version_info[0] > 2: basestring = (str, ) zip_longest = itertools.zip_longest else: zip_longest = itertools.izip_longest class Validator(object): """ JSON Schema validator. Can be used to validate any JSON document against a :class:`json_schema_validator.schema.Schema`. """ JSON_TYPE_MAP = { "string": basestring, "number": NUMERIC_TYPES, "integer": int, "object": dict, "array": list, "null": None.__class__, } def __init__(self): self._schema_stack = [] self._object_stack = [] def _push_object(self, obj, path): self._object_stack.append((obj, path)) def _pop_object(self): self._object_stack.pop() def _push_schema(self, schema, path): self._schema_stack.append((schema, path)) def _pop_schema(self): self._schema_stack.pop() @property def _object(self): return self._object_stack[-1][0] @property def _schema(self): return self._schema_stack[-1][0] @classmethod def validate(cls, schema, obj): """ Validate specified JSON object obj with specified schema. :param schema: Schema to validate against :type schema: :class:`json_schema_validator.schema.Schema` :param obj: JSON object to validate :rtype: bool :returns: True on success :raises `json_schema_validator.errors.ValidationError`: if the object does not match schema. :raises `json_schema_validator.errors.SchemaError`: if the schema itself is wrong. """ if not isinstance(schema, Schema): raise ValueError( "schema value {0!r} is not a Schema" " object".format(schema)) self = cls() self.validate_toplevel(schema, obj) return True def _get_object_expression(self): return "".join(map(lambda x: x[1], self._object_stack)) def _get_schema_expression(self): return "".join(map(lambda x: x[1], self._schema_stack)) def validate_toplevel(self, schema, obj): self._object_stack = [] self._schema_stack = [] self._push_schema(schema, "schema") self._push_object(obj, "object") self._validate() self._pop_schema() self._pop_object() def _validate(self): obj = self._object self._validate_type() self._validate_requires() if isinstance(obj, dict): self._validate_properties() self._validate_additional_properties() elif isinstance(obj, list): self._validate_items() else: self._validate_enum() self._validate_format() self._validate_pattern() if isinstance(obj, basestring): self._validate_length() elif isinstance(obj, NUMERIC_TYPES): self._validate_range() self._report_unsupported() def _report_error(self, legacy_message, new_message=None, schema_suffix=None): """ Report an error during validation. There are two error messages. The legacy message is used for backwards compatibility and usually contains the object (possibly very large) that failed to validate. The new message is much better as it contains just a short message on what went wrong. User code can inspect object_expr and schema_expr to see which part of the object failed to validate against which part of the schema. The schema_suffix, if provided, is appended to the schema_expr. This is quite handy to specify the bit that the validator looked at (such as the type or optional flag, etc). object_suffix serves the same purpose but is used for object expressions instead. """ object_expr = self._get_object_expression() schema_expr = self._get_schema_expression() if schema_suffix: schema_expr += schema_suffix raise ValidationError(legacy_message, new_message, object_expr, schema_expr) def _push_property_schema(self, prop): """Construct a sub-schema from a property of the current schema.""" schema = Schema(self._schema.properties[prop]) self._push_schema(schema, ".properties." + prop) def _push_additional_property_schema(self): schema = Schema(self._schema.additionalProperties) self._push_schema(schema, ".additionalProperties") def _push_array_schema(self): schema = Schema(self._schema.items) self._push_schema(schema, ".items") def _push_array_item_object(self, index): self._push_object(self._object[index], "[%d]" % index) def _push_property_object(self, prop): self._push_object(self._object[prop], "." + prop) def _report_unsupported(self): schema = self._schema if schema.contentEncoding is not None: raise NotImplementedError("contentEncoding is not supported") if schema.divisibleBy != 1: raise NotImplementedError("divisibleBy is not supported") if schema.disallow is not None: raise NotImplementedError("disallow is not supported") def _validate_type(self): schema = self._schema json_type = schema.type if json_type == "any": return obj = self._object if json_type == "boolean": # Bool is special cased because in python there is no # way to test for isinstance(something, bool) that would # not catch isinstance(1, bool) :/ if obj is not True and obj is not False: self._report_error( "{obj!r} does not match type {type!r}".format( obj=obj, type=json_type), "Object has incorrect type (expected boolean)", schema_suffix=".type") elif isinstance(json_type, dict): # Nested type check. This is pretty odd case. Here we # don't change our object stack (it's the same object). self._push_schema(Schema(json_type), ".type") self._validate() self._pop_schema() elif isinstance(json_type, list): # Alternative type check, here we may match _any_ of the types # in the list to be considered valid. json_type_list = json_type if json_type == []: return for index, json_type in enumerate(json_type_list): # Aww, ugly. The level of packaging around Schema is annoying self._push_schema( Schema({'type': json_type}), ".type.%d" % index) try: self._validate() except ValidationError: # Ignore errors, we just want one thing to match pass else: # We've got a match - break the loop break finally: # Pop the schema regardless of match/mismatch self._pop_schema() else: # We were not interupted (no break) so we did not match self._report_error( "{obj!r} does not match any of the types in {type!r}".format( obj=obj, type=json_type_list), "Object has incorrect type (multiple types possible)", schema_suffix=".type") else: # Simple type check if not isinstance(obj, self.JSON_TYPE_MAP[json_type]): self._report_error( "{obj!r} does not match type {type!r}".format( obj=obj, type=json_type), "Object has incorrect type (expected {type})".format( type=json_type), schema_suffix=".type") def _validate_pattern(self): ptn = self._schema.pattern obj = self._object if ptn is None: return if not isinstance(obj, basestring): return if re.match(ptn, obj): return self._report_error( "{obj!r} does not match pattern {ptn!r}".format( obj=obj,ptn=ptn), "Object does not match pattern (expected {ptn})".format( ptn=ptn), schema_suffix=".pattern" ) def _validate_format(self): fmt = self._schema.format obj = self._object if fmt is None: return if fmt == 'date-time': try: DATE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" datetime.datetime.strptime(obj, DATE_TIME_FORMAT) except ValueError: self._report_error( "{obj!r} is not a string representing JSON date-time".format( obj=obj), "Object is not a string representing JSON date-time", schema_suffix=".format") elif fmt == 'regex': try: re.compile(obj) except: self._report_error( "{obj!r} is not a string representing a regex".format( obj=obj), "Object is not a string representing a regex", schema_suffix=".format") else: raise NotImplementedError("format {0!r} is not supported".format(fmt)) def _validate_properties(self): obj = self._object schema = self._schema assert isinstance(obj, dict) for prop in schema.properties.keys(): self._push_property_schema(prop) if prop in obj: self._push_property_object(prop) self._validate() self._pop_object() else: if not self._schema.optional: self._report_error( "{obj!r} does not have property {prop!r}".format( obj=obj, prop=prop), "Object lacks property {prop!r}".format( prop=prop), schema_suffix=".optional") self._pop_schema() def _validate_additional_properties(self): obj = self._object assert isinstance(obj, dict) if self._schema.additionalProperties is False: # Additional properties are disallowed # Report exception for each unknown property for prop in obj.keys(): if prop not in self._schema.properties: self._report_error( "{obj!r} has unknown property {prop!r} and" " additionalProperties is false".format( obj=obj, prop=prop), "Object has unknown property {prop!r} but" " additional properties are disallowed".format( prop=prop), schema_suffix=".additionalProperties") else: # Check each property against this object self._push_additional_property_schema() for prop in obj.keys(): self._push_property_object(prop) self._validate() self._pop_object() self._pop_schema() def _validate_enum(self): obj = self._object schema = self._schema if schema.enum is not None: for allowed_value in schema.enum: if obj == allowed_value: break else: self._report_error( "{obj!r} does not match any value in enumeration" " {enum!r}".format(obj=obj, enum=schema.enum), "Object does not match any value in enumeration", schema_suffix=".enum") def _validate_length(self): obj = self._object schema = self._schema if schema.minLength is not None: if len(obj) < schema.minLength: self._report_error( "{obj!r} does not meet the minimum length" " {minLength!r}".format(obj=obj, minLength=schema.minLength), "Object does not meet the minimum length", schema_suffix=".minLength") if schema.maxLength is not None: if len(obj) > schema.maxLength: self._report_error( "{obj!r} exceeds the maximum length" " {maxLength!r}".format(obj=obj, maxLength=schema.maxLength), "Object exceeds the maximum length", schema_suffix=".maxLength") def _validate_range(self): obj = self._object schema = self._schema if schema.minimum is not None: if obj < schema.minimum or (obj == schema.minimum and not schema.minimumCanEqual): self._report_error( "{obj!r} is less than the minimum" " {minimum!r}".format(obj=obj, minimum=schema.minimum), "Object is less than the minimum", schema_suffix=".minimum") if schema.maximum is not None: if obj > schema.maximum or (obj == schema.maximum and not schema.maximumCanEqual): self._report_error( "{obj!r} is greater than the maximum" " {maximum!r}".format(obj=obj, maximum=schema.maximum), "Object is greater than the maximum", schema_suffix=".maximum") def _validate_items(self): obj = self._object schema = self._schema assert isinstance(obj, list) items_schema_json = schema.items if items_schema_json == {}: # default value, don't do anything return if isinstance(obj, list) and schema.uniqueItems is True and len(set(obj)) != len(obj): # If we want a list of unique items and the length of unique # elements is different from the length of the full list # then validation fails. # This implementation isn't strictly compatible with the specs, because # we are not checking unique dicts. self._report_error( "Repeated items found in {obj!r}".format(obj=obj), "Repeated items found in array", schema_suffix=".items") if schema.minItems: if len(obj) < schema.minItems: self._report_error( "{obj!r} has fewer than the minimum number of items" " {minItems!r}".format(obj=obj, minimum=schema.minItems), "Object has fewer than the minimum number of items", schema_suffix=".minItems") if schema.maxItems is not None: if len(obj) > schema.maxItems: self._report_error( "{obj!r} has more than the maximum number of items" " {maxItems!r}".format(obj=obj, minimum=schema.maxItems), "Object has more than the maximum number of items", schema_suffix=".maxItems") if isinstance(items_schema_json, dict): self._push_array_schema() for index, item in enumerate(obj): self._push_array_item_object(index) self._validate() self._pop_object() self._pop_schema() elif isinstance(items_schema_json, list): if len(obj) < len(items_schema_json): # If our data array is shorter than the schema then # validation fails. Longer arrays are okay (during this # step) as they are validated based on # additionalProperties schema self._report_error( "{obj!r} is shorter than array schema {schema!r}". format(obj=obj, schema=items_schema_json), "Object array is shorter than schema array", schema_suffix=".items") if len(obj) != len(items_schema_json) and schema.additionalProperties is False: # If our array is not exactly the same size as the # schema and additional properties are disallowed then # validation fails self._report_error( "{obj!r} is not of the same length as array schema" " {schema!r} and additionalProperties is" " false".format(obj=obj, schema=items_schema_json), "Object array is not of the same length as schema array", schema_suffix=".items") # Validate each array element using schema for the # corresponding array index, fill missing values (since # there may be more items in our array than in the schema) # with additionalProperties which by now is not False for index, (item, item_schema_json) in enumerate( zip_longest( obj, items_schema_json, fillvalue=schema.additionalProperties)): item_schema = Schema(item_schema_json) if index < len(items_schema_json): self._push_schema(item_schema, "items[%d]" % index) else: self._push_schema(item_schema, ".additionalProperties") self._push_array_item_object(index) self._validate() self._pop_schema() self._pop_object() def _validate_requires(self): obj = self._object schema = self._schema requires_json = schema.requires if requires_json == {}: # default value, don't do anything return # Find our enclosing object in the object stack if len(self._object_stack) < 2: self._report_error( "{obj!r} requires that enclosing object matches" " schema {schema!r} but there is no enclosing" " object".format(obj=obj, schema=requires_json), "Object has no enclosing object that matches schema", schema_suffix=".requires") # Note: Parent object can be None, (e.g. a null property) parent_obj = self._object_stack[-2][0] if isinstance(requires_json, basestring): # This is a simple property test if (not isinstance(parent_obj, dict) or requires_json not in parent_obj): self._report_error( "{obj!r} requires presence of property {requires!r}" " in the same object".format( obj=obj, requires=requires_json), "Enclosing object does not have property" " {prop!r}".format(prop=requires_json), schema_suffix=".requires") elif isinstance(requires_json, dict): # Requires designates a whole schema, the enclosing object # must match against that schema. # Here we resort to a small hack. Proper implementation # would require us to validate the parent object from its # own context (own list of parent objects). Since doing that # and restoring the state would be very complicated we just # instantiate a new validator with a subset of our current # history here. sub_validator = Validator() sub_validator._object_stack = self._object_stack[:-1] sub_validator._schema_stack = self._schema_stack[:] sub_validator._push_schema( Schema(requires_json), ".requires") sub_validator._validate() PK°dHH^7ø[SS!json_schema_validator/__init__.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """JSON Schema Validator.""" __version__ = (2, 4, 1, "final", 0) PK0¼FH{¹ÊÂh;h;json_schema_validator/schema.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """Helper module to work with raw JSON Schema.""" import re import sys from json_schema_validator.errors import SchemaError from json_schema_validator.misc import NUMERIC_TYPES if sys.version_info[0] > 2: basestring = (str, ) class Schema(object): """ JSON schema object. Schema describes aspects of a valid object. Upon validation each object has an associated schema. Various properties of the object are tested against rules described by the schema. """ def __init__(self, json_obj): """ Initialize a schema with a schema representation. :param json_obj: A JSON object (python dictionary) describing the schema. """ if not isinstance(json_obj, dict): raise SchemaError("Schema definition must be a JSON object") self._schema = json_obj def __repr__(self): return "Schema({0!r})".format(self._schema) @property def type(self): """ Type of a valid object. Type may be a JSON type name or a list of such names. Valid JSON type names are ``string``, ``number``, ``integer``, ``boolean``, ``object``, ``array``, ``any`` (default). """ value = self._schema.get("type", "any") if not isinstance(value, (basestring, dict, list)): raise SchemaError( "type value {0!r} is not a simple type name, nested " "schema nor a list of those".format(value)) if isinstance(value, list): type_list = value # Union types have to have at least two alternatives if len(type_list) < 2: raise SchemaError( "union type {0!r} is too short".format(value)) else: type_list = [value] seen = set() for js_type in type_list: if isinstance(js_type, dict): # no nested validation here pass elif isinstance(js_type, list): # no nested validation here pass else: if js_type in seen: raise SchemaError( ("type value {0!r} contains duplicate element" " {1!r}").format(value, js_type)) else: seen.add(js_type) if js_type not in ( "string", "number", "integer", "boolean", "object", "array", "null", "any"): raise SchemaError( "type value {0!r} is not a simple type " "name".format(js_type)) return value @property def properties(self): """Schema for particular properties of the object.""" value = self._schema.get("properties", {}) if not isinstance(value, dict): raise SchemaError( "properties value {0!r} is not an object".format(value)) return value @property def items(self): """ Schema or a list of schemas describing particular elements of the object. A single schema applies to all the elements. Each element of the object must match that schema. A list of schemas describes particular elements of the object. """ value = self._schema.get("items", {}) if not isinstance(value, (list, dict)): raise SchemaError( "items value {0!r} is neither a list nor an object". format(value)) return value @property def optional(self): """Flag indicating an optional property.""" value = self._schema.get("optional", False) if value is not False and value is not True: raise SchemaError( "optional value {0!r} is not a boolean".format(value)) return value @property def additionalProperties(self): """Schema for all additional properties, or False.""" value = self._schema.get("additionalProperties", {}) if not isinstance(value, dict) and value is not False: raise SchemaError( "additionalProperties value {0!r} is neither false nor" " an object".format(value)) return value @property def requires(self): """Additional object or objects required by this object.""" # NOTE: spec says this can also be a list of strings value = self._schema.get("requires", {}) if not isinstance(value, (basestring, dict)): raise SchemaError( "requires value {0!r} is neither a string nor an" " object".format(value)) return value @property def minimum(self): """Minimum value of the object.""" value = self._schema.get("minimum", None) if value is None: return if not isinstance(value, NUMERIC_TYPES): raise SchemaError( "minimum value {0!r} is not a numeric type".format( value)) return value @property def maximum(self): """Maximum value of the object.""" value = self._schema.get("maximum", None) if value is None: return if not isinstance(value, NUMERIC_TYPES): raise SchemaError( "maximum value {0!r} is not a numeric type".format( value)) return value @property def minimumCanEqual(self): """Flag indicating if maximum value is inclusive or exclusive.""" if self.minimum is None: raise SchemaError("minimumCanEqual requires presence of minimum") value = self._schema.get("minimumCanEqual", True) if value is not True and value is not False: raise SchemaError( "minimumCanEqual value {0!r} is not a boolean".format( value)) return value @property def maximumCanEqual(self): """Flag indicating if the minimum value is inclusive or exclusive.""" if self.maximum is None: raise SchemaError("maximumCanEqual requires presence of maximum") value = self._schema.get("maximumCanEqual", True) if value is not True and value is not False: raise SchemaError( "maximumCanEqual value {0!r} is not a boolean".format( value)) return value @property def minItems(self): """Minimum number of items in the collection.""" value = self._schema.get("minItems", 0) if not isinstance(value, int): raise SchemaError( "minItems value {0!r} is not an integer".format(value)) if value < 0: raise SchemaError( "minItems value {0!r} cannot be negative".format(value)) return value @property def maxItems(self): """Maximum number of items in the collection.""" value = self._schema.get("maxItems", None) if value is None: return if not isinstance(value, int): raise SchemaError( "maxItems value {0!r} is not an integer".format(value)) return value @property def uniqueItems(self): """Flag indicating that valid is a collection without duplicates.""" value = self._schema.get("uniqueItems", False) if value is not True and value is not False: raise SchemaError( "uniqueItems value {0!r} is not a boolean".format(value)) return value @property def pattern(self): """ Regular expression describing valid objects. .. note:: JSON schema specifications says that this value SHOULD follow the ``EMCA 262/Perl 5`` format. We cannot support this so we support python regular expressions instead. This is still valid but should be noted for clarity. :returns: None or compiled regular expression """ value = self._schema.get("pattern", None) if value is None: return try: return re.compile(value) except re.error as ex: raise SchemaError( "pattern value {0!r} is not a valid regular expression:" " {1}".format(value, str(ex))) @property def minLength(self): """Minimum length of object.""" value = self._schema.get("minLength", 0) if not isinstance(value, int): raise SchemaError( "minLength value {0!r} is not an integer".format(value)) if value < 0: raise SchemaError( "minLength value {0!r} cannot be negative".format(value)) return value @property def maxLength(self): """Maximum length of object.""" value = self._schema.get("maxLength", None) if value is None: return if not isinstance(value, int): raise SchemaError( "maxLength value {0!r} is not an integer".format(value)) return value @property def enum(self): """ Enumeration of allowed object values. The enumeration must not contain duplicates. """ value = self._schema.get("enum", None) if value is None: return if not isinstance(value, list): raise SchemaError( "enum value {0!r} is not a list".format(value)) if len(value) == 0: raise SchemaError( "enum value {0!r} does not contain any" " elements".format(value)) seen = set() for item in value: if item in seen: raise SchemaError( "enum value {0!r} contains duplicate element" " {1!r}".format(value, item)) else: seen.add(item) return value @property def title(self): """ Title of the object. This schema element is purely informative. """ value = self._schema.get("title", None) if value is None: return if not isinstance(value, basestring): raise SchemaError( "title value {0!r} is not a string".format(value)) return value @property def description(self): """ Description of the object. This schema element is purely informative. """ value = self._schema.get("description", None) if value is None: return if not isinstance(value, basestring): raise SchemaError( "description value {0!r} is not a string".format(value)) return value @property def format(self): """Format of the (string) object.""" value = self._schema.get("format", None) if value is None: return if not isinstance(value, basestring): raise SchemaError( "format value {0!r} is not a string".format(value)) if value in [ 'date-time', 'regex', ]: return value raise NotImplementedError( "format value {0!r} is not supported".format(value)) @property def contentEncoding(self): value = self._schema.get("contentEncoding", None) if value is None: return if value.lower() not in [ "7bit", "8bit", "binary", "quoted-printable", "base64", "ietf-token", "x-token"]: raise SchemaError( "contentEncoding value {0!r} is not" " valid".format(value)) if value.lower() != "base64": raise NotImplementedError( "contentEncoding value {0!r} is not supported".format( value)) return value @property def divisibleBy(self): """Integer that divides the object without reminder.""" value = self._schema.get("divisibleBy", 1) if value is None: return if not isinstance(value, NUMERIC_TYPES): raise SchemaError( "divisibleBy value {0!r} is not a numeric type". format(value)) if value < 0: raise SchemaError( "divisibleBy value {0!r} cannot be" " negative".format(value)) return value @property def disallow(self): """ Description of disallowed objects. Disallow must be a type name, a nested schema or a list of those. Type name must be one of ``string``, ``number``, ``integer``, ``boolean``, ``object``, ``array``, ``null`` or ``any``. """ value = self._schema.get("disallow", None) if value is None: return if not isinstance(value, (basestring, dict, list)): raise SchemaError( "disallow value {0!r} is not a simple type name, nested " "schema nor a list of those".format(value)) if isinstance(value, list): disallow_list = value else: disallow_list = [value] seen = set() for js_disallow in disallow_list: if isinstance(js_disallow, dict): # no nested validation here pass else: if js_disallow in seen: raise SchemaError( "disallow value {0!r} contains duplicate element" " {1!r}".format(value, js_disallow)) else: seen.add(js_disallow) if js_disallow not in ( "string", "number", "integer", "boolean", "object", "array", "null", "any"): raise SchemaError( "disallow value {0!r} is not a simple type" " name".format(js_disallow)) return disallow_list @property def extends(self): raise NotImplementedError("extends property is not supported") @property def default(self): """Default value for an object.""" try: return self._schema["default"] except KeyError: raise SchemaError("There is no schema default for this item") PKdHHí8§§json_schema_validator/misc.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """Stuff that does not belong anywhere else.""" import decimal # List of types recognized as numeric NUMERIC_TYPES = (int, float, decimal.Decimal) PKVdHHÇt< "json_schema_validator/shortcuts.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """One liners that make the code shorter.""" try: import simplejson as json except ImportError: import json from json_schema_validator.schema import Schema from json_schema_validator.validator import Validator _default_deserializer = json.loads def validate(schema_text, data_text, deserializer=_default_deserializer): """ Validate specified JSON text with specified schema. Both arguments are converted to JSON objects with :func:`simplejson.loads`, if present, or :func:`json.loads`. :param schema_text: Text of the JSON schema to check against :type schema_text: :class:`str` :param data_text: Text of the JSON object to check :type data_text: :class:`str` :param deserializer: Function to convert the schema and data to JSON objects :type deserializer: :class:`callable` :returns: Same as :meth:`json_schema_validator.validator.Validator.validate` :raises: Whatever may be raised by simplejson (in particular :class:`simplejson.decoder.JSONDecoderError`, a subclass of :class:`ValueError`) or json :raises: Whatever may be raised by :meth:`json_schema_validator.validator.Validator.validate`. In particular :class:`json_schema_validator.errors.ValidationError` and :class:`json_schema_validator.errors.SchemaError` """ schema = Schema(deserializer(schema_text)) data = deserializer(data_text) return Validator.validate(schema, data) PKdHHsELöªªjson_schema_validator/errors.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """Error classes used by this package.""" class SchemaError(ValueError): """Exception raised when there is a problem with the schema itself.""" class ValidationError(ValueError): """ Exception raised on mismatch between the validated object and the schema. .. attribute:: message Old and verbose message that contains less helpful message and lots of JSON data (deprecated). .. attribute:: new_message Short and concise message about the problem. .. attribute:: object_expr A JavaScript expression that evaluates to the object that failed to validate. The expression always starts with a root object called ``'object'``. .. attribute:: schema_expr A JavaScript expression that evaluates to the schema that was checked at the time validation failed. The expression always starts with a root object called ``'schema'``. """ def __init__(self, message, new_message=None, object_expr=None, schema_expr=None): self.message = message self.new_message = new_message self.object_expr = object_expr self.schema_expr = schema_expr def __str__(self): return ("ValidationError: {0} " "object_expr={1!r}, " "schema_expr={2!r})").format( self.new_message, self.object_expr, self.schema_expr) PK)¼FH;æèP P #json_schema_validator/extensions.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """ Date-time extension, allows to serialize and deserialize datetime objects in a consistent way. Implements equivalent of schema: { "type": "string", "format": "date-time" } """ import re import sys from datetime import datetime, timedelta if sys.version_info[0] > 2: basestring = (str, ) class datetime_extension(object): """ Proxy class for serializing datetime.datetime objects. The serialization is a JSON string. Date is encoded using the ISO 8601 format: YYYY-MM-DDThh:mm:ssZ That is: * Four digit year code * Dash * Two digit month code * Dash * Two digit day code * Capital letter 'T' - time stamp indicator * Two digit hour code * Colon * Two digit minute code * Colon * Two digit seconds code * Capital letter 'Z' - Zulu (UTC) time zone indicator """ FORMAT = "%Y-%m-%dT%H:%M:%SZ" @classmethod def to_json(cls, obj): return obj.strftime(cls.FORMAT) @classmethod def from_json(cls, doc): return datetime.strptime(doc, cls.FORMAT) class timedelta_extension(object): """ Proxy for serializing datetime.timedelta instances """ PATTERN = re.compile("^(\d+)d (\d+)s (\d+)us$") @classmethod def to_json(cls, obj): """ Serialize wrapped datetime.timedelta instance to a string the with the following format: [DAYS]d [SECONDS]s [MICROSECONDS]us """ return "{0}d {1}s {2}us".format( obj.days, obj.seconds, obj.microseconds) @classmethod def from_json(cls, doc): """ Deserialize JSON document (string) to datetime.timedelta instance """ if not isinstance(doc, basestring): raise TypeError("JSON document must be a string") match = cls.PATTERN.match(doc) if not match: raise ValueError("JSON document must match expected pattern") days, seconds, microseconds = map(int, match.groups()) return timedelta(days, seconds, microseconds) PK+dHH$›*€b€b*json_schema_validator/tests/test_schema.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """ Unit tests for JSON schema """ import json import sys from testscenarios import TestWithScenarios from testtools import TestCase from json_schema_validator.errors import SchemaError from json_schema_validator.schema import Schema PY2 = sys.version_info[0] == 2 PY35 = sys.version_info[0:2] >= (3, 5) if PY2: import yaml deserializer = yaml.safe_load else: deserializer = json.loads class SchemaTests(TestWithScenarios, TestCase): scenarios = [ ('type_default', { 'schema': '{}', 'expected': { 'type': 'any' }, }), ('type_string', { 'schema': '{"type": "string"}', 'expected': { 'type': 'string' }, }), ('type_number', { 'schema': '{"type": "number"}', 'expected': { 'type': 'number' }, }), ('type_integer', { 'schema': '{"type": "integer"}', 'expected': { 'type': 'integer' }, }), ('type_boolean', { 'schema': '{"type": "boolean"}', 'expected': { 'type': 'boolean' }, }), ('type_object', { 'schema': '{"type": "object"}', 'expected': { 'type': 'object' }, }), ('type_array', { 'schema': '{"type": "array"}', 'expected': { 'type': 'array' }, }), ('type_complex_subtype', { 'schema': '{"type": {}}', 'expected': { 'type': {}, }, }), ("type_empty_list", { 'schema': '{"type": []}', 'access': 'type', 'raises': SchemaError("union type [] is too short") }), ("type_list_with_one_item", { 'schema': '{"type": ["number"]}', 'access': 'type', 'raises': SchemaError("union type ['number'] is too short") }), ('type_list', { 'schema': '{"type": ["string", "number"]}', 'expected': { 'type': ["string", "number"], }, }), ('type_wrong_type', { 'schema': '{"type": 5}', 'access': 'type', 'raises': SchemaError( "type value 5 is not a simple type name," " nested schema nor a list of those"), }), ('type_not_a_simple_type_name', { 'schema': '{"type": "foobar"}', 'access': 'type', 'raises': SchemaError( "type value 'foobar' is not a simple type name"), }), ('type_list_duplicates', { 'schema': '{"type": ["string", "string"]}', 'access': 'type', 'raises': SchemaError( "type value ['string', 'string'] contains duplicate" " element 'string'") }), ('properties_default', { 'schema': '{}', 'expected': { 'properties': {}, }, }), ('properties_example', { 'schema': '{"properties": {"prop": {"type": "number"}}}', 'expected': { 'properties': {"prop": {"type": "number"}}, }, }), ('properties_wrong_type', { 'schema': '{"properties": 5}', 'access': 'properties', 'raises': SchemaError( 'properties value 5 is not an object'), }), ('items_default', { 'schema': '{}', 'expected': { 'items': {}, }, }), ('items_tuple', { 'schema': '{"items": [{}, {}]}', 'expected': { 'items': [{}, {}], }, }), ('items_each', { 'schema': '{"items": {"type": "number"}}', 'expected': { 'items': {"type": "number"}, }, }), ('items_wrong_type', { 'schema': '{"items": 5}', 'access': 'items', 'raises': SchemaError( 'items value 5 is neither a list nor an object'), }), ('optional_default', { 'schema': '{}', 'expected': { 'optional': False, }, }), ('optional_true', { 'schema': '{"optional": true}', 'expected': { 'optional': True, }, }), ('optional_false', { 'schema': '{"optional": false}', 'expected': { 'optional': False, }, }), ('optional_wrong_type', { 'schema': '{"optional": 5}', 'access': 'optional', 'raises': SchemaError( 'optional value 5 is not a boolean'), }), ('additionalProperties_default', { 'schema': '{}', 'expected': { 'additionalProperties': {} }, }), ('additionalProperties_false', { 'schema': '{"additionalProperties": false}', 'expected': { "additionalProperties": False, }, }), ('additionalProperties_object', { 'schema': '{"additionalProperties": {"type": "number"}}', 'expected': { "additionalProperties": {"type": "number"}, }, }), ('additionalProperties_wrong_type', { 'schema': '{"additionalProperties": 5}', 'access': 'additionalProperties', 'raises': SchemaError( 'additionalProperties value 5 is neither false nor an' ' object'), }), ('requires_default', { 'schema': '{}', 'expected': { 'requires': {}, }, }), ('requires_property_name', { 'schema': '{"requires": "other"}', 'expected': { 'requires': "other", }, }), ('requires_schema', { 'schema': '{"requires": {"properties": {"other": {"type": "number"}}}}', 'expected': { 'requires': { 'properties': { 'other': { 'type': 'number' }, }, }, }, }), ('requires_wrong_value', { 'schema': '{"requires": 5}', 'access': 'requires', 'raises': SchemaError( 'requires value 5 is neither a string nor an object'), }), ('minimum_default', { 'schema': '{}', 'expected': { 'minimum': None }, }), ('minimum_integer', { 'schema': '{"minimum": 5}', 'expected': { 'minimum': 5 }, }), ('minimum_float', { 'schema': '{"minimum": 3.5}', 'expected': { 'minimum': 3.5 }, }), ('minimum_wrong_type', { 'schema': '{"minimum": "foobar"}', 'access': 'minimum', 'raises': SchemaError( 'minimum value \'foobar\' is not a numeric type') }), ('maximum_default', { 'schema': '{}', 'expected': { 'maximum': None }, }), ('maximum_integer', { 'schema': '{"maximum": 5}', 'expected': { 'maximum': 5 }, }), ('maximum_float', { 'schema': '{"maximum": 3.5}', 'expected': { 'maximum': 3.5 }, }), ('maximum_wrong_type', { 'schema': '{"maximum": "foobar"}', 'access': 'maximum', 'raises': SchemaError( 'maximum value \'foobar\' is not a numeric type') }), ('minimumCanEqual_default', { 'schema': '{"minimum": 5}', 'expected': { 'minimum': 5, 'minimumCanEqual': True }, }), ('minimumCanEqual_false', { 'schema': '{"minimum": 5, "minimumCanEqual": false}', 'expected': { 'minimum': 5, 'minimumCanEqual': False, }, }), ('minimumCanEqual_true', { 'schema': '{"minimum": 5, "minimumCanEqual": true}', 'expected': { 'minimum': 5, 'minimumCanEqual': True }, }), ('minimumCanEqual_without_minimum', { 'schema': '{}', 'access': 'minimumCanEqual', 'raises': SchemaError( "minimumCanEqual requires presence of minimum"), }), ('minimumCanEqual_wrong_type', { 'schema': '{"minimum": 5, "minimumCanEqual": 5}', 'access': 'minimumCanEqual', 'raises': SchemaError( "minimumCanEqual value 5 is not a boolean"), }), ('maximumCanEqual_default', { 'schema': '{"maximum": 5}', 'expected': { 'maximum': 5, 'maximumCanEqual': True }, }), ('maximumCanEqual_false', { 'schema': '{"maximum": 5, "maximumCanEqual": false}', 'expected': { 'maximum': 5, 'maximumCanEqual': False, }, }), ('maximumCanEqual_true', { 'schema': '{"maximum": 5, "maximumCanEqual": true}', 'expected': { 'maximum': 5, 'maximumCanEqual': True }, }), ('maximumCanEqual_without_maximum', { 'schema': '{}', 'access': 'maximumCanEqual', 'raises': SchemaError( "maximumCanEqual requires presence of maximum"), }), ('maximumCanEqual_wrong_type', { 'schema': '{"maximum": 5, "maximumCanEqual": 5}', 'access': 'maximumCanEqual', 'raises': SchemaError( "maximumCanEqual value 5 is not a boolean"), }), ("minItems_default", { 'schema': '{}', 'expected': { 'minItems': 0, }, }), ("minItems_integer", { 'schema': '{"minItems": 13}', 'expected': { 'minItems': 13, }, }), ("minItems_zero", { 'schema': '{"minItems": 0}', 'expected': { 'minItems': 0, }, }), ("minItems_minus_one", { 'schema': '{"minItems": -1}', 'access': 'minItems', 'raises': SchemaError( "minItems value -1 cannot be negative"), }), ("minItems_wrong_type", { 'schema': '{"minItems": "foobar"}', 'access': 'minItems', 'raises': SchemaError( "minItems value 'foobar' is not an integer"), }), ("maxItems_default", { 'schema': '{}', 'expected': { 'maxItems': None, }, }), ("maxItems_integer", { 'schema': '{"maxItems": 13}', 'expected': { 'maxItems': 13, }, }), ("maxItems_zero", { 'schema': '{"maxItems": 0}', 'expected': { 'maxItems': 0, }, }), ("maxItems_minus_one", { 'schema': '{"maxItems": -1}', 'expected': { 'maxItems': -1 }, }), ("maxItems_wrong_type", { 'schema': '{"maxItems": "foobar"}', 'access': 'maxItems', 'raises': SchemaError( "maxItems value 'foobar' is not an integer"), }), ("uniqueItems_default", { 'schema': '{}', 'expected': { 'uniqueItems': False } }), ("uniqueItems_true", { 'schema': '{"uniqueItems": true}', 'expected': { 'uniqueItems': True } }), ("uniqueItems_false", { 'schema': '{"uniqueItems": false}', 'expected': { 'uniqueItems': False } }), ("uniqueItems_wrong_type", { 'schema': '{"uniqueItems": 5}', 'access': 'uniqueItems', 'raises': SchemaError( "uniqueItems value 5 is not a boolean") }), ("pattern_default", { 'schema': '{}', 'expected': { 'pattern': None, }, }), #("pattern_simple", { # 'schema': '{"pattern": "foo|bar"}', # 'expected': { # 'pattern': re.compile('foo|bar'), # }, #}), ("pattern_broken", { 'schema': '{"pattern": "[unterminated"}', 'access': 'pattern', 'raises': SchemaError( "pattern value '[unterminated' is not a valid regular" " expression: " + ("unexpected end of regular expression" if not PY35 else "unterminated character set at position 0" )), }), ("minLength_default", { 'schema': '{}', 'expected': { 'minLength': 0, }, }), ("minLength_integer", { 'schema': '{"minLength": 13}', 'expected': { 'minLength': 13, }, }), ("minLength_zero", { 'schema': '{"minLength": 0}', 'expected': { 'minLength': 0, }, }), ("minLength_minus_one", { 'schema': '{"minLength": -1}', 'access': 'minLength', 'raises': SchemaError( "minLength value -1 cannot be negative"), }), ("minLength_wrong_type", { 'schema': '{"minLength": "foobar"}', 'access': 'minLength', 'raises': SchemaError( "minLength value 'foobar' is not an integer"), }), ("maxLength_default", { 'schema': '{}', 'expected': { 'maxLength': None, }, }), ("maxLength_integer", { 'schema': '{"maxLength": 13}', 'expected': { 'maxLength': 13, }, }), ("maxLength_zero", { 'schema': '{"maxLength": 0}', 'expected': { 'maxLength': 0, }, }), ("maxLength_minus_one", { 'schema': '{"maxLength": -1}', 'expected': { 'maxLength': -1 }, }), ("maxLength_wrong_type", { 'schema': '{"maxLength": "foobar"}', 'access': 'maxLength', 'raises': SchemaError( "maxLength value 'foobar' is not an integer"), }), ("enum_default", { 'schema': '{}', 'expected': { 'enum': None, } }), ("enum_simple", { 'schema': '{"enum": ["foo", "bar"]}', 'expected': { 'enum': ["foo", "bar"], } }), ("enum_mixed", { 'schema': '{"enum": [5, false, "foobar"]}', 'expected': { 'enum':[5, False, "foobar"] } }), ("enum_wrong_type", { 'schema': '{"enum": "foobar"}', 'access': 'enum', 'raises': SchemaError( "enum value 'foobar' is not a list"), }), ("enum_too_short", { 'schema': '{"enum": []}', 'access': 'enum', 'raises': SchemaError( "enum value [] does not contain any elements"), }), ("enum_duplicates", { 'schema': '{"enum": ["foo", "foo"]}', 'access': 'enum', 'raises': SchemaError( "enum value ['foo', 'foo'] contains duplicate element" " 'foo'"), }), ("title_default", { 'schema': '{}', 'expected': { 'title': None, }, }), ("title_simple", { 'schema': '{"title": "foobar"}', 'expected': { 'title': "foobar", }, }), ("title_wrong_type", { 'schema': '{"title": 5}', 'access': 'title', 'raises': SchemaError('title value 5 is not a string') }), ("description_default", { 'schema': '{}', 'expected': { 'description': None, }, }), ("description_simple", { 'schema': '{"description": "foobar"}', 'expected': { 'description': "foobar", }, }), ("description_wrong_type", { 'schema': '{"description": 5}', 'access': 'description', 'raises': SchemaError('description value 5 is not a string') }), ("format_default", { 'schema': '{}', 'expected': { 'format': None }, }), ("format_date_time", { 'schema': '{"format": "date-time"}', 'expected': { 'format': "date-time" }, }), ("format_regex", { 'schema': '{"format": "regex"}', 'expected': { 'format': "regex" }, }), ("format_wrong_type", { 'schema': '{"format": 5}', 'access': 'format', 'raises': SchemaError('format value 5 is not a string') }), ("format_not_implemented", { 'schema': '{"format": "color"}', 'access': 'format', 'raises': NotImplementedError( "format value 'color' is not supported") }), ("contentEncoding_default", { 'schema': '{}', 'expected': { 'contentEncoding': None, } }), ("contentEncoding_base64", { 'schema': '{"contentEncoding": "base64"}', 'expected': { 'contentEncoding': "base64", }, }), ("contentEncoding_base64_mixed_case", { 'schema': '{"contentEncoding": "BAsE64"}', 'expected': { 'contentEncoding': 'BAsE64', }, }), ("contentEncoding_unsupported_value", { 'schema': '{"contentEncoding": "x-token"}', 'access': 'contentEncoding', 'raises': NotImplementedError( "contentEncoding value 'x-token' is not supported") }), ("contentEncoding_unknown_value", { 'schema': '{"contentEncoding": "bogus"}', 'access': 'contentEncoding', 'raises': SchemaError( "contentEncoding value 'bogus' is not valid") }), ("divisibleBy_default", { 'schema': '{}', 'expected': { 'divisibleBy': 1 } }), ("divisibleBy_int", { 'schema': '{"divisibleBy": 5}', 'expected': { 'divisibleBy': 5 } }), ("divisibleBy_float", { 'schema': '{"divisibleBy": 3.5}', 'expected': { 'divisibleBy': 3.5 } }), ("divisibleBy_wrong_type", { 'schema': '{"divisibleBy": "foobar"}', 'access': 'divisibleBy', 'raises': SchemaError( "divisibleBy value 'foobar' is not a numeric type") }), ("divisibleBy_minus_one", { 'schema': '{"divisibleBy": -1}', 'access': 'divisibleBy', 'raises': SchemaError( "divisibleBy value -1 cannot be negative") }), ('disallow_default', { 'schema': '{}', 'expected': { 'disallow': None }, }), ('disallow_string', { 'schema': '{"disallow": "string"}', 'expected': { 'disallow': ['string'] }, }), ('disallow_number', { 'schema': '{"disallow": "number"}', 'expected': { 'disallow': ['number'] }, }), ('disallow_integer', { 'schema': '{"disallow": "integer"}', 'expected': { 'disallow': ['integer'] }, }), ('disallow_boolean', { 'schema': '{"disallow": "boolean"}', 'expected': { 'disallow': ['boolean'] }, }), ('disallow_object', { 'schema': '{"disallow": "object"}', 'expected': { 'disallow': ['object'] }, }), ('disallow_array', { 'schema': '{"disallow": "array"}', 'expected': { 'disallow': ['array'] }, }), ('disallow_complex_subtype', { 'schema': '{"disallow": {}}', 'expected': { 'disallow': [{}], }, }), ('disallow_list', { 'schema': '{"disallow": ["string", "number"]}', 'expected': { 'disallow': ["string", "number"], }, }), ('disallow_wrong_type', { 'schema': '{"disallow": 5}', 'access': 'disallow', 'raises': SchemaError( "disallow value 5 is not a simple type name," " nested schema nor a list of those"), }), ('disallow_not_a_simple_disallow_name', { 'schema': '{"disallow": "foobar"}', 'access': 'disallow', 'raises': SchemaError( "disallow value 'foobar' is not a simple type name") }), ('disallow_list_duplicates', { 'schema': '{"disallow": ["string", "string"]}', 'access': 'disallow', 'raises': SchemaError( "disallow value ['string', 'string'] contains" " duplicate element 'string'") }), ('extends_not_supported', { 'schema': '{}', 'access': 'extends', 'raises': NotImplementedError( "extends property is not supported"), }), ('default_with_value', { 'schema': '{"default": 5}', 'expected': { 'default': 5 } }), ('default_without_value', { 'schema': '{}', 'access': 'default', 'raises': SchemaError("There is no schema default for this item"), }), ] def test_schema_attribute(self): if deserializer != json.loads: # Always check the serialised JSON using the native JSON loader # so that any error messages are consistent and appropriate. json.loads(self.schema) schema = Schema(deserializer(self.schema)) if hasattr(self, 'expected'): for attr, expected_value in self.expected.items(): self.assertEqual( expected_value, getattr(schema, attr)) elif hasattr(self, 'access') and hasattr(self, 'raises'): self.assertRaises( type(self.raises), getattr, schema, self.access) try: getattr(schema, self.access) except type(self.raises) as ex: self.assertEqual(str(ex), str(self.raises)) except Exception as ex: self.fail("Raised exception {0!r} instead of {1!r}".format( ex, self.raises)) else: self.fail("Broken test definition, must define 'expected' " "or 'access' and 'raises' scenario attributes") PK ¼FH›ûçÓ††'json_schema_validator/tests/__init__.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """ Package with unit tests for json-schema-validator """ import doctest import unittest def app_modules(): return [ 'json_schema_validator', 'json_schema_validator.errors', 'json_schema_validator.extensions', 'json_schema_validator.misc', 'json_schema_validator.schema', 'json_schema_validator.shortcuts', 'json_schema_validator.validator', ] def test_modules(): return [ 'json_schema_validator.tests.test_extensions', 'json_schema_validator.tests.test_schema', 'json_schema_validator.tests.test_validator', ] def test_suite(): """ Build an unittest.TestSuite() object with all the tests in _modules. Each module is harvested for both regular unittests and doctests """ modules = app_modules() + test_modules() suite = unittest.TestSuite() loader = unittest.TestLoader() for name in modules: __import__(name, fromlist=['']) tests = loader.loadTestsFromName(name) suite.addTests(tests) doctests = doctest.DocTestSuite(name) suite.addTests(doctests) return suite PK¼FHn1[”‘‘.json_schema_validator/tests/test_extensions.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """ Unit tests for JSON extensions """ from testtools import TestCase from datetime import datetime, timedelta from json_schema_validator.extensions import datetime_extension, timedelta_extension class ExtensionTests(object): def test_to_json(self): text = self.extension.to_json(self.reference_obj) self.assertEqual(text, self.reference_text) def test_from_json(self): obj = self.extension.from_json(self.reference_text) self.assertEqual(obj, self.reference_obj) class DatetimeExtensionTests(TestCase, ExtensionTests): reference_obj = datetime(2010, 12, 7, 23, 59, 58) reference_text = "2010-12-07T23:59:58Z" extension = datetime_extension class TimedeltaExtensionTests(TestCase, ExtensionTests): reference_obj = timedelta(days=1, seconds=2, microseconds=3) reference_text = "1d 2s 3us" extension = timedelta_extension PK+dHHZ¿ÖÚÂmÂm-json_schema_validator/tests/test_validator.py# Copyright (C) 2010, 2011 Linaro Limited # Copyright (C) 2016 Zygmunt Krynicki # # Author: Zygmunt Krynicki # # This file is part of json-schema-validator. # # json-schema-validator is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 # as published by the Free Software Foundation # # json-schema-validator is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with json-schema-validator. If not, see . """ Unit tests for JSON schema """ import functools import json import sys from testscenarios import TestWithScenarios from testtools import TestCase from json_schema_validator.errors import ValidationError from json_schema_validator.shortcuts import validate from json_schema_validator.validator import Validator PY2 = sys.version_info[0] == 2 if PY2: import yaml def deserializer(json_string): # Always check the serialised JSON using the native JSON loader # so that any error messages are consistent and appropriate. json.loads(json_string) return yaml.safe_load(json_string) validate = functools.partial(validate, deserializer=deserializer) class ValidatorFailureTests(TestWithScenarios, TestCase): scenarios = [ ("type_string_got_null", { 'schema': '{"type": "string"}', 'data': 'null', 'raises': ValidationError( "None does not match type 'string'", "Object has incorrect type (expected string)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_string_got_integer", { 'schema': '{"type": "string"}', 'data': '5', 'raises': ValidationError( "5 does not match type 'string'", "Object has incorrect type (expected string)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_number_got_null", { 'schema': '{"type": "number"}', 'data': 'null', 'raises': ValidationError( "None does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_number_got_string", { 'schema': '{"type": "number"}', 'data': '"foobar"', 'raises': ValidationError( "'foobar' does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_number_got_string_that_looks_like_number", { 'schema': '{"type": "number"}', 'data': '"3"', 'raises': ValidationError( "'3' does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_integer_got_float", { 'schema': '{"type": "integer"}', 'data': '1.5', 'raises': ValidationError( "1.5 does not match type 'integer'", "Object has incorrect type (expected integer)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_integer_got_null", { 'schema': '{"type": "integer"}', 'data': 'null', 'raises': ValidationError( "None does not match type 'integer'", "Object has incorrect type (expected integer)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_boolean_got_null", { 'schema': '{"type": "boolean"}', 'data': 'null', 'raises': ValidationError( "None does not match type 'boolean'", "Object has incorrect type (expected boolean)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_boolean_got_empty_string", { 'schema': '{"type": "boolean"}', 'data': '""', 'raises': ValidationError( "'' does not match type 'boolean'", "Object has incorrect type (expected boolean)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_boolean_got_empty_list", { 'schema': '{"type": "boolean"}', 'data': '[]', 'raises': ValidationError( "[] does not match type 'boolean'", "Object has incorrect type (expected boolean)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_boolean_got_empty_object", { 'schema': '{"type": "boolean"}', 'data': '{}', 'raises': ValidationError( "{} does not match type 'boolean'", "Object has incorrect type (expected boolean)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_object_got_integer", { 'schema': '{"type": "object"}', 'data': '1', 'raises': ValidationError( "1 does not match type 'object'", "Object has incorrect type (expected object)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_object_got_null", { 'schema': '{"type": "object"}', 'data': 'null', 'raises': ValidationError( "None does not match type 'object'", "Object has incorrect type (expected object)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_array_got_null", { 'schema': '{"type": "array"}', 'data': 'null', 'raises': ValidationError( "None does not match type 'array'", "Object has incorrect type (expected array)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_array_got_integer", { 'schema': '{"type": "array"}', 'data': '1', 'raises': ValidationError( "1 does not match type 'array'", "Object has incorrect type (expected array)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_null_got_empty_string", { 'schema': '{"type": "null"}', 'data': '""', 'raises': ValidationError( "'' does not match type 'null'", "Object has incorrect type (expected null)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_null_got_zero", { 'schema': '{"type": "null"}', 'data': '0', 'raises': ValidationError( "0 does not match type 'null'", "Object has incorrect type (expected null)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_null_got_empty_list", { 'schema': '{"type": "null"}', 'data': '[]', 'raises': ValidationError( "[] does not match type 'null'", "Object has incorrect type (expected null)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_null_got_empty_object", { 'schema': '{"type": "null"}', 'data': '{}', 'raises': ValidationError( "{} does not match type 'null'", "Object has incorrect type (expected null)"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("type_list_got_mismatching_item", { 'schema': '{"type": ["string", "number"]}', 'data': '{}', 'raises': ValidationError( "{} does not match any of the types in ['string', 'number']", "Object has incorrect type (multiple types possible)", ".type"), 'object_expr': 'object', 'schema_expr': 'schema.type', }), ("property_check_is_not_primitive", { 'schema': """ { "type": "object", "properties": { "foo": { "type": "number" } } }""", 'data': '{"foo": "foobar"}', 'raises': ValidationError( "'foobar' does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object.foo', 'schema_expr': 'schema.properties.foo.type', }), ("property_check_validates_optional_properties", { 'schema': """ { "type": "object", "properties": { "foo": { "type": "number", "optional": true } } }""", 'data': '{"foo": null}', 'raises': ValidationError( "None does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object.foo', 'schema_expr': 'schema.properties.foo.type', }), ("property_check_reports_missing_non_optional_properties", { 'schema': """ { "type": "object", "properties": { "foo": { "type": "number", "optional": false } } }""", 'data': '{}', 'raises': ValidationError( "{} does not have property 'foo'", "Object lacks property 'foo'"), 'object_expr': 'object', 'schema_expr': 'schema.properties.foo.optional', }), ("property_check_reports_unknown_properties_when_additionalProperties_is_false", { 'schema': """ { "type": "object", "additionalProperties": false }""", 'data': '{"foo": 5}', 'raises': ValidationError( "{'foo': 5} has unknown property 'foo' and" " additionalProperties is false", "Object has unknown property 'foo' but additional " "properties are disallowed"), 'object_expr': 'object', 'schema_expr': 'schema.additionalProperties', }), ("property_check_validates_additional_properties_using_specified_type_when_additionalProperties_is_an_object_violation", { 'schema': """ { "type": "object", "additionalProperties": { "type": "string" } }""", 'data': '{"foo": "aaa", "bar": 5}', 'raises': ValidationError( "5 does not match type 'string'", "Object has incorrect type (expected string)"), 'object_expr': 'object.bar', 'schema_expr': 'schema.additionalProperties.type', }), ("enum_check_reports_unlisted_values", { 'schema': '{"enum": [1, 2, 3]}', 'data': '5', 'raises': ValidationError( '5 does not match any value in enumeration [1, 2, 3]', "Object does not match any value in enumeration"), 'object_expr': 'object', 'schema_expr': 'schema.enum', }), ("items_with_single_schema_finds_problems", { 'schema': '{"items": {"type": "string"}}', 'data': '["foo", null, "froz"]', 'raises': ValidationError( "None does not match type 'string'", "Object has incorrect type (expected string)"), 'object_expr': 'object[1]', 'schema_expr': 'schema.items.type', }), ("items_with_array_schema_checks_for_too_short_data", { 'schema': """ { "items": [ {"type": "string"}, {"type": "boolean"} ] }""", 'data': '["foo"]', 'raises': ValidationError( "['foo'] is shorter than array schema [{'type':" " 'string'}, {'type': 'boolean'}]", "Object array is shorter than schema array"), 'object_expr': 'object', 'schema_expr': 'schema.items', }), ("items_with_array_schema_and_additionalProperties_of_false_checks_for_too_much_data", { 'schema': """ { "items": [ {"type": "string"}, {"type": "boolean"} ], "additionalProperties": false }""", 'data': '["foo", false, 5]', 'raises': ValidationError( "['foo', False, 5] is not of the same length as array" " schema [{'type': 'string'}, {'type': 'boolean'}] and" " additionalProperties is false", "Object array is not of the same length as schema array"), 'object_expr': 'object', 'schema_expr': 'schema.items', }), ("items_with_array_schema_and_additionalProperties_can_find_problems", { 'schema': """ { "items": [ {"type": "string"}, {"type": "boolean"} ], "additionalProperties": { "type": "number" } }""", 'data': '["foo", false, 5, 7.9, null]', 'raises': ValidationError( "None does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object[4]', 'schema_expr': 'schema.additionalProperties.type', }), ("array_with_array_schema_and_uniqueItems_is_True", { 'schema': """ { "type": "array", "items": {"type": "string"}, "uniqueItems": true }""", 'data': '["foo", "bar", "foo"]', 'raises': ValidationError( "Repeated items found in ['foo', 'bar', 'foo']", "Repeated items found in array"), 'object_expr': 'object', 'schema_expr': 'schema.items', }), ("requires_with_simple_property_name_can_report_problems", { 'schema': """ { "properties": { "foo": { "optional": true }, "bar": { "requires": "foo", "optional": true } } } """, 'data': '{"bar": null}', 'raises': ValidationError( "None requires presence of property 'foo' in the same" " object", "Enclosing object does not have property 'foo'"), 'object_expr': 'object.bar', 'schema_expr': 'schema.properties.bar.requires', }), ("requires_with_simple_property_name_can_report_problems_while_nested", { 'schema': """ { "type": "object", "properties": { "nested": { "properties": { "foo": { "optional": true }, "bar": { "requires": "foo", "optional": true } } } } } """, 'data': '{"nested": {"bar": null}}', 'raises': ValidationError( "None requires presence of property 'foo' in the same" " object", "Enclosing object does not have property 'foo'"), 'object_expr': 'object.nested.bar', 'schema_expr': 'schema.properties.nested.properties.bar.requires', }), ("requires_with_schema_can_report_problems", { 'schema': """ { "properties": { "foo": { "optional": true }, "bar": { "requires": { "properties": { "foo": { "type": "number" } } }, "optional": true } } } """, 'data': '{"bar": null}', 'raises': ValidationError( "{'bar': None} does not have property 'foo'", "Object lacks property 'foo'"), 'object_expr': 'object', 'schema_expr': 'schema.properties.bar.requires.properties.foo.optional', }), ("requires_with_schema_can_report_subtle_problems", { # In this test presence of "bar" requires that "foo" is # present and has a type "number" 'schema': """ { "properties": { "foo": { "optional": true }, "bar": { "requires": { "properties": { "foo": { "type": "number" } } }, "optional": true } } } """, 'data': '{"bar": null, "foo": "not a number"}', 'raises': ValidationError( "'not a number' does not match type 'number'", "Object has incorrect type (expected number)"), 'object_expr': 'object.foo', 'schema_expr': 'schema.properties.bar.requires.properties.foo.type' }), ("format_date_time_finds_problems", { 'schema': '{"format": "date-time"}', 'data': '"broken"', 'raises': ValidationError( "'broken' is not a string representing JSON date-time", "Object is not a string representing JSON date-time"), 'object_expr': 'object', 'schema_expr': 'schema.format' }), ] def test_validation_error_has_proper_message(self): ex = self.assertRaises(ValidationError, validate, self.schema, self.data) self.assertEqual(ex.message, self.raises.message) def test_validation_error_has_proper_new_message(self): ex = self.assertRaises(ValidationError, validate, self.schema, self.data) self.assertEqual(ex.new_message, self.raises.new_message) def test_validation_error_has_proper_object_expr(self): ex = self.assertRaises(ValidationError, validate, self.schema, self.data) self.assertEqual(ex.object_expr, self.object_expr) def test_validation_error_has_proper_schema_expr(self): ex = self.assertRaises(ValidationError, validate, self.schema, self.data) self.assertEqual(ex.schema_expr, self.schema_expr) class ValidatorSuccessTests(TestWithScenarios, TestCase): scenarios = [ ("type_string_got_string", { 'schema': '{"type": "string"}', 'data': '"foobar"' }), ("type_number_got_integer", { 'schema': '{"type": "number"}', 'data': '1', }), ("type_number_number_float", { 'schema': '{"type": "number"}', 'data': '1.1', }), ("type_integer_got_integer_one", { 'schema': '{"type": "integer"}', 'data': '1' }), ("type_integer_got_integer", { 'schema': '{"type": "integer"}', 'data': '5' }), ("type_boolean_got_true", { 'schema': '{"type": "boolean"}', 'data': 'true', }), ("type_boolean_got_false", { 'schema': '{"type": "boolean"}', 'data': 'true', }), ("type_object_got_object", { 'schema': '{"type": "object"}', 'data': '{}' }), ("type_array_got_array", { 'schema': '{"type": "array"}', 'data': '[]' }), ("type_null_got_null", { 'schema': '{"type": "null"}', 'data': 'null', }), ("type_any_got_null", { 'schema': '{"type": "any"}', 'data': 'null', }), ("type_any_got_integer", { 'schema': '{"type": "any"}', 'data': '5', }), ("type_any_got_boolean", { 'schema': '{"type": "any"}', 'data': 'false', }), ("type_any_got_string", { 'schema': '{"type": "any"}', 'data': '"foobar"', }), ("type_any_got_array", { 'schema': '{"type": "any"}', 'data': '[]', }), ("type_any_got_object", { 'schema': '{"type": "any"}', 'data': '{}', }), ("type_nested_schema_check", { 'schema': '{"type": {"type": "number"}}', 'data': '5', }), ("type_list_with_two_values_got_first_value", { 'schema': '{"type": ["number", "string"]}', 'data': '1', }), ("type_list_with_two_values_got_second_value", { 'schema': '{"type": ["number", "string"]}', 'data': '"string"', }), ("property_ignored_on_non_objects", { 'schema': '{"properties": {"foo": {"type": "number"}}}', 'data': '"foobar"', }), ("property_checks_known_props", { 'schema': """ { "type": "object", "properties": { "foo": { "type": "number" }, "bar": { "type": "boolean" } } }""", 'data': """ { "foo": 5, "bar": false }""" }), ("property_check_ignores_missing_optional_properties", { 'schema': """ { "type": "object", "properties": { "foo": { "type": "number", "optional": true } } }""", 'data': '{}', }), ("property_check_ignores_normal_properties_when_additionalProperties_is_false", { 'schema': """ { "type": "object", "properties": { "foo": {} }, "additionalProperties": false }""", 'data': '{"foo": 5}', }), ("property_check_validates_additional_properties_using_specified_type_when_additionalProperties_is_an_object", { 'schema': """ { "type": "object", "additionalProperties": { "type": "string" } }""", 'data': '{"foo": "aaa", "bar": "bbb"}', }), ("enum_check_does_nothing_by_default", { 'schema': '{}', 'data': '5', }), ("enum_check_verifies_possible_values", { 'schema': '{"enum": [1, 2, 3]}', 'data': '2', }), ("items_check_does_nothing_for_non_arrays", { 'schema': '{"items": {"type": "string"}}', 'data': '5', }), ("items_with_single_schema_applies_to_each_item", { 'schema': '{"items": {"type": "string"}}', 'data': '["foo", "bar", "froz"]', }), ("items_with_array_schema_applies_to_corresponding_items", { 'schema': """ { "items": [ {"type": "string"}, {"type": "boolean"} ] }""", 'data': '["foo", true]', }), ("items_with_array_schema_and_additionalProperties", { 'schema': """ { "items": [ {"type": "string"}, {"type": "boolean"} ], "additionalProperties": { "type": "number" } }""", 'data': '["foo", false, 5, 7.9]', }), ("requires_with_simple_property_name_does_nothing_when_parent_property_is_not_used", { 'schema': """ { "properties": { "foo": { "optional": true }, "bar": { "requires": "foo", "optional": true } } } """, 'data': '{}', }), ("requires_with_simple_property_name_works_when_condition_satisfied", { 'schema': """ { "properties": { "foo": { "optional": true }, "bar": { "requires": "foo", "optional": true } } } """, 'data': '{"foo": null, "bar": null}', }), ("requires_with_schema_name_does_nothing_when_parent_property_is_not_used", { 'schema': """ { "properties": { "foo": { "optional": true }, "bar": { "requires": { "properties": { "foo": { "type": "number" } } }, "optional": true } } } """, 'data': '{}', }), ("format_date_time_works", { 'schema': '{"format": "date-time"}', 'data': '"2010-11-12T14:38:55Z"', }), ("array_with_array_schema_and_uniqueItems_is_True", { 'schema': """ { "type": "array", "items": {"type": "string"}, "uniqueItems": true }""", 'data': '["foo", "bar", "baz"]', }), ] def test_validator_does_not_raise_an_exception(self): self.assertEqual( True, validate(self.schema, self.data)) PKùdHH^-Ò 5json_schema_validator-2.4.1.dist-info/DESCRIPTION.rstUNKNOWN PKùdHHm¬"a//3json_schema_validator-2.4.1.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Operating System :: OS Independent", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy"], "extensions": {"python.details": {"contacts": [{"email": "me@zygoon.pl", "name": "Zygmunt Krynicki", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/zyga/json-schema-validator"}}}, "generator": "bdist_wheel (0.26.0)", "metadata_version": "2.0", "name": "json-schema-validator", "summary": "JSON Schema Validator", "test_requires": [{"requires": ["PyYaml", "testscenarios (>=0.1)", "testtools (>=0.9.2)"]}], "version": "2.4.1"}PKødHH4b3json_schema_validator-2.4.1.dist-info/top_level.txtjson_schema_validator PKÍ­FH“×2.json_schema_validator-2.4.1.dist-info/zip-safe PKùdHHìndªnn+json_schema_validator-2.4.1.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PKùdHHø^<Г“.json_schema_validator-2.4.1.dist-info/METADATAMetadata-Version: 2.0 Name: json-schema-validator Version: 2.4.1 Summary: JSON Schema Validator Home-page: https://github.com/zyga/json-schema-validator Author: Zygmunt Krynicki Author-email: me@zygoon.pl License: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy UNKNOWN PKùdHHÑ[*T´´,json_schema_validator-2.4.1.dist-info/RECORDjson_schema_validator/__init__.py,sha256=pcX7PBA7lFTsO9Mzw_ujjYUI9TI-QRPBbCIleJeVClc,851 json_schema_validator/errors.py,sha256=rnno8rf4O8kseG4J68RcRy_sM6QfLEsP_UBksPSpdeY,2218 json_schema_validator/extensions.py,sha256=efnkCYNj0o_FMbBbQMw4ihdunU7ie1Sd-hVdoOA4cBI,2896 json_schema_validator/misc.py,sha256=REpRjTTAi3HT4Hc7XaMulBBqnZp_o0VHgT3JRfVHCZY,935 json_schema_validator/schema.py,sha256=JmtAqPLYFspx12_XjF_xjiVudisTin4SJ0XTbkwx7CQ,15208 json_schema_validator/shortcuts.py,sha256=mZ2pevki_kcmnAtUT-_5FgQtQfmAAj-pvvDAFNx9eRk,2317 json_schema_validator/validator.py,sha256=4M4qJ2EGgeh4HnvfpLygA1xfQ1HUWKSWeqEAZ0pldME,21222 json_schema_validator/tests/__init__.py,sha256=9vQMsITKILrIYmMnsqIBFlz6bie3MVnSlUWDdURUPjc,1926 json_schema_validator/tests/test_extensions.py,sha256=CxX3NJIYjFOlzcpA4WhuQaXaCsL721KdV7oP-sFgh6s,1681 json_schema_validator/tests/test_schema.py,sha256=e0mVv5mYAAeozA__dcp_c3TfcxBej7QozLP8CrB3AG0,25216 json_schema_validator/tests/test_validator.py,sha256=zFtW3XpMCU8h8tZwdxQMTlqmT9-5K9aFF5YM8HRBxus,28098 json_schema_validator-2.4.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 json_schema_validator-2.4.1.dist-info/METADATA,sha256=MAjKi2AoSMRoVBxYD0uow_Caf-bmcv9EzJ8VrvViZOk,915 json_schema_validator-2.4.1.dist-info/RECORD,, json_schema_validator-2.4.1.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 json_schema_validator-2.4.1.dist-info/metadata.json,sha256=3GMVd0iCdyq36GQvcTK8pFkLg7zjPQFQgF8yMe8XWZI,1071 json_schema_validator-2.4.1.dist-info/top_level.txt,sha256=oiAvAYA_cTs-aa8WrfYYm4Jb8gOg4-CkvVqrHnl9EpM,22 json_schema_validator-2.4.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 PK5¼FH™nŒ2æRæR"json_schema_validator/validator.pyPK°dHH^7ø[SS!&Sjson_schema_validator/__init__.pyPK0¼FH{¹ÊÂh;h;¸Vjson_schema_validator/schema.pyPKdHHí8§§]’json_schema_validator/misc.pyPKVdHHÇt< "?–json_schema_validator/shortcuts.pyPKdHHsELöªªŒŸjson_schema_validator/errors.pyPK)¼FH;æèP P #s¨json_schema_validator/extensions.pyPK+dHH$›*€b€b*´json_schema_validator/tests/test_schema.pyPK ¼FH›ûçÓ††'Ìjson_schema_validator/tests/__init__.pyPK¼FHn1[”‘‘.—json_schema_validator/tests/test_extensions.pyPK+dHHZ¿ÖÚÂmÂm-t%json_schema_validator/tests/test_validator.pyPKùdHH^-Ò 5“json_schema_validator-2.4.1.dist-info/DESCRIPTION.rstPKùdHHm¬"a//3Þ“json_schema_validator-2.4.1.dist-info/metadata.jsonPKødHH4b3^˜json_schema_validator-2.4.1.dist-info/top_level.txtPKÍ­FH“×2.Řjson_schema_validator-2.4.1.dist-info/zip-safePKùdHHìndªnn+™json_schema_validator-2.4.1.dist-info/WHEELPKùdHHø^<Г“.É™json_schema_validator-2.4.1.dist-info/METADATAPKùdHHÑ[*T´´,¨json_schema_validator-2.4.1.dist-info/RECORDPK¦¤