From 5b280913c1efdbedc72b7fd9f9e6ed85b14f3fd3 Mon Sep 17 00:00:00 2001 From: Kiall Mac Innes Date: Thu, 31 Oct 2013 12:34:05 +0000 Subject: [PATCH] Ensure we're compatible with JSONSchema 1.3.0 through 2.2.0 Change-Id: I551241c84112727eb55b0fb65f97fd532f064207 --- designate/schema/__init__.py | 7 +- designate/schema/_validators.py | 87 ++++++++++++++++++++++ designate/schema/validators.py | 128 ++++++++++---------------------- 3 files changed, 132 insertions(+), 90 deletions(-) create mode 100644 designate/schema/_validators.py diff --git a/designate/schema/__init__.py b/designate/schema/__init__.py index f6a9879df..290d2f79d 100644 --- a/designate/schema/__init__.py +++ b/designate/schema/__init__.py @@ -18,6 +18,7 @@ from designate import exceptions from designate import utils from designate.schema import validators from designate.schema import resolvers +from designate.schema import format LOG = logging.getLogger(__name__) @@ -30,10 +31,12 @@ class Schema(object): if version == 'v1': self.validator = validators.Draft3Validator( - self.raw_schema, resolver=self.resolver) + self.raw_schema, resolver=self.resolver, + format_checker=format.draft3_format_checker) elif version == 'v2': self.validator = validators.Draft4Validator( - self.raw_schema, resolver=self.resolver) + self.raw_schema, resolver=self.resolver, + format_checker=format.draft4_format_checker) else: raise Exception('Unknown API version: %s' % version) diff --git a/designate/schema/_validators.py b/designate/schema/_validators.py new file mode 100644 index 000000000..0b9fde53f --- /dev/null +++ b/designate/schema/_validators.py @@ -0,0 +1,87 @@ +# Copyright 2013 Hewlett-Packard Development Company, L.P. +# +# Author: Kiall Mac Innes +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import datetime +import jsonschema + +try: + # JSONSchema 2+ + from jsonschema import _utils + ensure_list = _utils.ensure_list + types_msg = _utils.types_msg +except ImportError: + # JSONSchema 1.3 + ensure_list = jsonschema._list + types_msg = jsonschema._types_msg + + +def type_draft3(validator, types, instance, schema): + types = ensure_list(types) + + # NOTE(kiall): A datetime object is not a string, but is still valid. + if ('format' in schema and schema['format'] == 'date-time' + and isinstance(instance, datetime.datetime)): + return + + all_errors = [] + for index, type in enumerate(types): + if type == "any": + return + if validator.is_type(type, "object"): + errors = list(validator.descend(instance, type, schema_path=index)) + if not errors: + return + all_errors.extend(errors) + else: + if validator.is_type(instance, type): + return + else: + yield jsonschema.ValidationError( + types_msg(instance, types), context=all_errors, + ) + + +def oneOf_draft3(validator, oneOf, instance, schema): + # Backported from Draft4 to Draft3 + subschemas = iter(oneOf) + first_valid = next( + (s for s in subschemas if validator.is_valid(instance, s)), None, + ) + + if first_valid is None: + yield jsonschema.ValidationError( + "%r is not valid under any of the given schemas." % (instance,) + ) + else: + more_valid = [s for s in subschemas + if validator.is_valid(instance, s)] + if more_valid: + more_valid.append(first_valid) + reprs = ", ".join(repr(schema) for schema in more_valid) + yield jsonschema.ValidationError( + "%r is valid under each of %s" % (instance, reprs) + ) + + +def type_draft4(validator, types, instance, schema): + types = ensure_list(types) + + # NOTE(kiall): A datetime object is not a string, but is still valid. + if ('format' in schema and schema['format'] == 'date-time' + and isinstance(instance, datetime.datetime)): + return + + if not any(validator.is_type(instance, type) for type in types): + yield jsonschema.ValidationError(types_msg(instance, types)) diff --git a/designate/schema/validators.py b/designate/schema/validators.py index 2bc4edf8c..d2b6250d1 100644 --- a/designate/schema/validators.py +++ b/designate/schema/validators.py @@ -13,104 +13,56 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -import datetime import jsonschema -from jsonschema import _utils -import jsonschema.validators from designate.openstack.common import log as logging -from designate.schema import format +from designate.schema import _validators LOG = logging.getLogger(__name__) -def type_draft3(validator, types, instance, schema): - types = _utils.ensure_list(types) - - # NOTE(kiall): A datetime object is not a string, but is still valid. - if ('format' in schema and schema['format'] == 'date-time' - and isinstance(instance, datetime.datetime)): - return - - all_errors = [] - for index, type in enumerate(types): - if type == "any": - return - if validator.is_type(type, "object"): - errors = list(validator.descend(instance, type, schema_path=index)) - if not errors: - return - all_errors.extend(errors) - else: - if validator.is_type(instance, type): - return - else: - yield jsonschema.ValidationError( - _utils.types_msg(instance, types), context=all_errors, - ) +# JSONSchema 1.3 to 2.0 compatibility +try: + # JSONSchema 2+ + from jsonschema import _utils # flake8: noqa + from jsonschema import validators + JS2 = True + Draft3ValidatorBase = validators.Draft3Validator + Draft4ValidatorBase = validators.Draft4Validator +except ImportError: + # JSONSchema 1.3 + JS2 = False + Draft3ValidatorBase = jsonschema.Draft3Validator + Draft4ValidatorBase = jsonschema.Draft4Validator -def oneOf_draft3(self, oneOf, instance, schema): - # Backported from Draft4 to Draft3 - subschemas = iter(oneOf) - first_valid = next( - (s for s in subschemas if self.is_valid(instance, s)), None, - ) +if JS2: + Draft3Validator = validators.extend( + Draft3ValidatorBase, + validators={ + "type": _validators.type_draft3, + "oneOf": _validators.oneOf_draft3, + }) - if first_valid is None: - yield jsonschema.ValidationError( - "%r is not valid under any of the given schemas." % (instance,) - ) - else: - more_valid = [s for s in subschemas if self.is_valid(instance, s)] - if more_valid: - more_valid.append(first_valid) - reprs = ", ".join(repr(schema) for schema in more_valid) - yield jsonschema.ValidationError( - "%r is valid under each of %s" % (instance, reprs) - ) + Draft4Validator = validators.extend( + Draft4ValidatorBase, + validators={ + "type": _validators.type_draft4, + }) +else: + class Draft3Validator(Draft3ValidatorBase): + def validate_type(self, types, instance, schema): + for i in _validators.type_draft3(self, types, instance, + schema): + yield i -def type_draft4(validator, types, instance, schema): - types = _utils.ensure_list(types) + def validate_oneOf(self, oneOf, instance, schema): + for i in _validators.oneOf_draft3(self, oneOf, instance, schema): + yield i - # NOTE(kiall): A datetime object is not a string, but is still valid. - if ('format' in schema and schema['format'] == 'date-time' - and isinstance(instance, datetime.datetime)): - return - - if not any(validator.is_type(instance, type) for type in types): - yield jsonschema.ValidationError(_utils.types_msg(instance, types)) - - -Draft3Validator = jsonschema.validators.extend( - jsonschema.validators.Draft3Validator, - validators={ - "type": type_draft3, - "oneOf": oneOf_draft3, - }) - - -Draft4Validator = jsonschema.validators.extend( - jsonschema.validators.Draft4Validator, - validators={ - "type": type_draft4, - }) - - -class Draft4Validator(Draft4Validator): - def __init__(self, schema, types=(), resolver=None, format_checker=None): - if format_checker is None: - format_checker = format.draft4_format_checker - - super(Draft4Validator, self).__init__(schema, types, resolver, - format_checker) - - -class Draft3Validator(Draft3Validator): - def __init__(self, schema, types=(), resolver=None, format_checker=None): - if format_checker is None: - format_checker = format.draft3_format_checker - - super(Draft3Validator, self).__init__(schema, types, resolver, - format_checker) + class Draft4Validator(Draft4ValidatorBase): + def validate_type(self, types, instance, schema): + for i in _validators.type_draft4(self, types, instance, + schema): + yield i