diff --git a/README.rst b/README.rst index 7befbcb..a338e63 100644 --- a/README.rst +++ b/README.rst @@ -324,4 +324,5 @@ You can use RequestsOpenAPIResponse as a Requests response factory: Related projects ################ * `openapi-spec-validator `__ +* `openapi-schema-validator `__ * `pyramid_openapi3 `__ diff --git a/openapi_core/schema_validator/__init__.py b/openapi_core/schema_validator/__init__.py deleted file mode 100644 index a86664a..0000000 --- a/openapi_core/schema_validator/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from openapi_core.schema_validator._format import oas30_format_checker -from openapi_core.schema_validator.validators import OAS30Validator - -__all__ = ['OAS30Validator', 'oas30_format_checker'] diff --git a/openapi_core/schema_validator/_format.py b/openapi_core/schema_validator/_format.py deleted file mode 100644 index 35f0653..0000000 --- a/openapi_core/schema_validator/_format.py +++ /dev/null @@ -1,135 +0,0 @@ -from base64 import b64encode, b64decode -import binascii -from datetime import datetime -from uuid import UUID - -from jsonschema._format import FormatChecker -from jsonschema.exceptions import FormatError -from six import binary_type, text_type, integer_types - -DATETIME_HAS_STRICT_RFC3339 = False -DATETIME_HAS_ISODATE = False -DATETIME_RAISES = () - -try: - import isodate -except ImportError: - pass -else: - DATETIME_HAS_ISODATE = True - DATETIME_RAISES += (ValueError, isodate.ISO8601Error) - -try: - import strict_rfc3339 -except ImportError: - pass -else: - DATETIME_HAS_STRICT_RFC3339 = True - DATETIME_RAISES += (ValueError, TypeError) - - -def is_int32(instance): - return isinstance(instance, integer_types) - - -def is_int64(instance): - return isinstance(instance, integer_types) - - -def is_float(instance): - return isinstance(instance, float) - - -def is_double(instance): - # float has double precision in Python - # It's double in CPython and Jython - return isinstance(instance, float) - - -def is_binary(instance): - return isinstance(instance, binary_type) - - -def is_byte(instance): - if isinstance(instance, text_type): - instance = instance.encode() - - try: - return b64encode(b64decode(instance)) == instance - except TypeError: - return False - - -def is_datetime(instance): - if not isinstance(instance, (binary_type, text_type)): - return False - - if DATETIME_HAS_STRICT_RFC3339: - return strict_rfc3339.validate_rfc3339(instance) - - if DATETIME_HAS_ISODATE: - return isodate.parse_datetime(instance) - - return True - - -def is_date(instance): - if not isinstance(instance, (binary_type, text_type)): - return False - - if isinstance(instance, binary_type): - instance = instance.decode() - - return datetime.strptime(instance, "%Y-%m-%d") - - -def is_uuid(instance): - if not isinstance(instance, (binary_type, text_type)): - return False - - if isinstance(instance, binary_type): - instance = instance.decode() - - return text_type(UUID(instance)) == instance - - -def is_password(instance): - return True - - -class OASFormatChecker(FormatChecker): - - checkers = { - 'int32': (is_int32, ()), - 'int64': (is_int64, ()), - 'float': (is_float, ()), - 'double': (is_double, ()), - 'byte': (is_byte, (binascii.Error, TypeError)), - 'binary': (is_binary, ()), - 'date': (is_date, (ValueError, )), - 'date-time': (is_datetime, DATETIME_RAISES), - 'password': (is_password, ()), - # non standard - 'uuid': (is_uuid, (AttributeError, ValueError)), - } - - def check(self, instance, format): - if format not in self.checkers: - raise FormatError( - "Format checker for %r format not found" % (format, )) - - func, raises = self.checkers[format] - result, cause = None, None - try: - result = func(instance) - except raises as e: - cause = e - - if not result: - raise FormatError( - "%r is not a %r" % (instance, format), cause=cause, - ) - return result - - -oas30_format_checker = OASFormatChecker() diff --git a/openapi_core/schema_validator/_types.py b/openapi_core/schema_validator/_types.py deleted file mode 100644 index b647dfc..0000000 --- a/openapi_core/schema_validator/_types.py +++ /dev/null @@ -1,21 +0,0 @@ -from jsonschema._types import ( - TypeChecker, is_array, is_bool, is_integer, - is_object, is_number, -) -from six import text_type, binary_type - - -def is_string(checker, instance): - return isinstance(instance, (text_type, binary_type)) - - -oas30_type_checker = TypeChecker( - { - u"string": is_string, - u"number": is_number, - u"integer": is_integer, - u"boolean": is_bool, - u"array": is_array, - u"object": is_object, - }, -) diff --git a/openapi_core/schema_validator/_validators.py b/openapi_core/schema_validator/_validators.py deleted file mode 100644 index fdcdeae..0000000 --- a/openapi_core/schema_validator/_validators.py +++ /dev/null @@ -1,87 +0,0 @@ -from jsonschema._utils import find_additional_properties, extras_msg -from jsonschema.exceptions import ValidationError, FormatError - - -def type(validator, data_type, instance, schema): - if instance is None: - return - - if not validator.is_type(instance, data_type): - yield ValidationError("%r is not of type %s" % (instance, data_type)) - - -def format(validator, format, instance, schema): - if instance is None: - return - - if validator.format_checker is not None: - try: - validator.format_checker.check(instance, format) - except FormatError as error: - yield ValidationError(error.message, cause=error.cause) - - -def items(validator, items, instance, schema): - if not validator.is_type(instance, "array"): - return - - for index, item in enumerate(instance): - for error in validator.descend(item, items, path=index): - yield error - - -def nullable(validator, is_nullable, instance, schema): - if instance is None and not is_nullable: - yield ValidationError("None for not nullable") - - -def required(validator, required, instance, schema): - if not validator.is_type(instance, "object"): - return - for property in required: - if property not in instance: - prop_schema = schema['properties'][property] - read_only = prop_schema.get('readOnly', False) - write_only = prop_schema.get('writeOnly', False) - if validator.write and read_only or validator.read and write_only: - continue - yield ValidationError("%r is a required property" % property) - - -def additionalProperties(validator, aP, instance, schema): - if not validator.is_type(instance, "object"): - return - - extras = set(find_additional_properties(instance, schema)) - - if not extras: - return - - if validator.is_type(aP, "object"): - for extra in extras: - for error in validator.descend(instance[extra], aP, path=extra): - yield error - elif validator.is_type(aP, "boolean"): - if not aP: - error = "Additional properties are not allowed (%s %s unexpected)" - yield ValidationError(error % extras_msg(extras)) - - -def readOnly(validator, ro, instance, schema): - if not validator.write or not ro: - return - - yield ValidationError( - "Tried to write read-only proparty with %s" % (instance)) - - -def writeOnly(validator, wo, instance, schema): - if not validator.read or not wo: - return - - yield ValidationError( - "Tried to read write-only proparty with %s" % (instance)) - - -def not_implemented(validator, value, instance, schema): - pass diff --git a/openapi_core/schema_validator/validators.py b/openapi_core/schema_validator/validators.py deleted file mode 100644 index 198d42a..0000000 --- a/openapi_core/schema_validator/validators.py +++ /dev/null @@ -1,72 +0,0 @@ -from jsonschema import _legacy_validators, _utils, _validators -from jsonschema.validators import create - -from openapi_core.schema_validator import _types as oas_types -from openapi_core.schema_validator import _validators as oas_validators - - -BaseOAS30Validator = create( - meta_schema=_utils.load_schema("draft4"), - validators={ - u"multipleOf": _validators.multipleOf, - # exclusiveMaximum supported inside maximum_draft3_draft4 - u"maximum": _legacy_validators.maximum_draft3_draft4, - # exclusiveMinimum supported inside minimum_draft3_draft4 - u"minimum": _legacy_validators.minimum_draft3_draft4, - u"maxLength": _validators.maxLength, - u"minLength": _validators.minLength, - u"pattern": _validators.pattern, - u"maxItems": _validators.maxItems, - u"minItems": _validators.minItems, - u"uniqueItems": _validators.uniqueItems, - u"maxProperties": _validators.maxProperties, - u"minProperties": _validators.minProperties, - u"enum": _validators.enum, - # adjusted to OAS - u"type": oas_validators.type, - u"allOf": _validators.allOf, - u"oneOf": _validators.oneOf, - u"anyOf": _validators.anyOf, - u"not": _validators.not_, - u"items": oas_validators.items, - u"properties": _validators.properties, - u"required": oas_validators.required, - u"additionalProperties": oas_validators.additionalProperties, - # TODO: adjust description - u"format": oas_validators.format, - # TODO: adjust default - u"$ref": _validators.ref, - # fixed OAS fields - u"nullable": oas_validators.nullable, - u"discriminator": oas_validators.not_implemented, - u"readOnly": oas_validators.readOnly, - u"writeOnly": oas_validators.writeOnly, - u"xml": oas_validators.not_implemented, - u"externalDocs": oas_validators.not_implemented, - u"example": oas_validators.not_implemented, - u"deprecated": oas_validators.not_implemented, - }, - type_checker=oas_types.oas30_type_checker, - version="oas30", - id_of=lambda schema: schema.get(u"id", ""), -) - - -class OAS30Validator(BaseOAS30Validator): - - def __init__(self, *args, **kwargs): - self.read = kwargs.pop('read', None) - self.write = kwargs.pop('write', None) - super(OAS30Validator, self).__init__(*args, **kwargs) - - def iter_errors(self, instance, _schema=None): - if _schema is None: - _schema = self.schema - - # append defaults to trigger validator (i.e. nullable) - if 'nullable' not in _schema: - _schema.update({ - 'nullable': False, - }) - - return super(OAS30Validator, self).iter_errors(instance, _schema) diff --git a/openapi_core/unmarshalling/schemas/factories.py b/openapi_core/unmarshalling/schemas/factories.py index 3c699a0..0952d00 100644 --- a/openapi_core/unmarshalling/schemas/factories.py +++ b/openapi_core/unmarshalling/schemas/factories.py @@ -1,10 +1,10 @@ from copy import deepcopy import warnings +from openapi_schema_validator import OAS30Validator, oas30_format_checker + from openapi_core.schema.schemas.enums import SchemaType, SchemaFormat from openapi_core.schema.schemas.models import Schema -from openapi_core.schema_validator import OAS30Validator -from openapi_core.schema_validator import oas30_format_checker from openapi_core.unmarshalling.schemas.enums import UnmarshalContext from openapi_core.unmarshalling.schemas.exceptions import ( FormatterNotFoundError, diff --git a/openapi_core/unmarshalling/schemas/unmarshallers.py b/openapi_core/unmarshalling/schemas/unmarshallers.py index 95913db..5f924d4 100644 --- a/openapi_core/unmarshalling/schemas/unmarshallers.py +++ b/openapi_core/unmarshalling/schemas/unmarshallers.py @@ -1,6 +1,11 @@ from functools import partial import logging +from openapi_schema_validator._types import ( + is_array, is_bool, is_integer, + is_object, is_number, is_string, +) +from openapi_schema_validator._format import oas30_format_checker from six import text_type, binary_type from six import iteritems @@ -8,11 +13,6 @@ from openapi_core.extensions.models.factories import ModelFactory from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType from openapi_core.schema.schemas.models import Schema from openapi_core.schema.schemas.types import NoValue -from openapi_core.schema_validator._types import ( - is_array, is_bool, is_integer, - is_object, is_number, is_string, -) -from openapi_core.schema_validator._format import oas30_format_checker from openapi_core.unmarshalling.schemas.enums import UnmarshalContext from openapi_core.unmarshalling.schemas.exceptions import ( UnmarshalError, ValidateError, InvalidSchemaValue, diff --git a/requirements.txt b/requirements.txt index b70e99e..36d15a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,7 @@ openapi-spec-validator +openapi-schema-validator six lazy-object-proxy -strict_rfc3339 -isodate attrs parse==1.14.0 more-itertools>=5.0.0 diff --git a/requirements_2.7.txt b/requirements_2.7.txt index c911221..ff492fb 100644 --- a/requirements_2.7.txt +++ b/requirements_2.7.txt @@ -1,9 +1,9 @@ openapi-spec-validator +openapi-schema-validator six lazy-object-proxy backports.functools-lru-cache backports.functools-partialmethod enum34 -strict_rfc3339 attrs more-itertools==5.0.0 diff --git a/setup.cfg b/setup.cfg index 3c96c3b..c181fac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,10 +24,9 @@ setup_requires = setuptools install_requires = openapi-spec-validator + openapi-schema-validator six lazy-object-proxy - strict_rfc3339 - isodate attrs werkzeug parse diff --git a/tests/unit/unmarshalling/test_validate.py b/tests/unit/unmarshalling/test_validate.py index 22cedf9..d7e5787 100644 --- a/tests/unit/unmarshalling/test_validate.py +++ b/tests/unit/unmarshalling/test_validate.py @@ -312,11 +312,11 @@ class TestSchemaValidate(object): u('2018-01-02T23:59:59Z'), ]) @mock.patch( - 'openapi_core.schema_validator._format.' + 'openapi_schema_validator._format.' 'DATETIME_HAS_STRICT_RFC3339', True ) @mock.patch( - 'openapi_core.schema_validator._format.' + 'openapi_schema_validator._format.' 'DATETIME_HAS_ISODATE', False ) def test_string_format_datetime_strict_rfc3339( @@ -332,11 +332,11 @@ class TestSchemaValidate(object): u('2018-01-02T23:59:59Z'), ]) @mock.patch( - 'openapi_core.schema_validator._format.' + 'openapi_schema_validator._format.' 'DATETIME_HAS_STRICT_RFC3339', False ) @mock.patch( - 'openapi_core.schema_validator._format.' + 'openapi_schema_validator._format.' 'DATETIME_HAS_ISODATE', True ) def test_string_format_datetime_isodate(self, value, validator_factory):