forked from python-openapi/openapi-schema-validator
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
541 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
include LICENSE | ||
include README.rst | ||
include requirements.txt | ||
include requirements_dev.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
************************ | ||
openapi-schema-validator | ||
************************ | ||
|
||
.. image:: https://img.shields.io/pypi/v/openapi-schema-validator.svg | ||
:target: https://pypi.python.org/pypi/openapi-schema-validator | ||
.. image:: https://travis-ci.org/p1c2u/openapi-schema-validator.svg?branch=master | ||
:target: https://travis-ci.org/p1c2u/openapi-schema-validator | ||
.. image:: https://img.shields.io/codecov/c/github/p1c2u/openapi-schema-validator/master.svg?style=flat | ||
:target: https://codecov.io/github/p1c2u/openapi-schema-validator?branch=master | ||
.. image:: https://img.shields.io/pypi/pyversions/openapi-schema-validator.svg | ||
:target: https://pypi.python.org/pypi/openapi-schema-validator | ||
.. image:: https://img.shields.io/pypi/format/openapi-schema-validator.svg | ||
:target: https://pypi.python.org/pypi/openapi-schema-validator | ||
.. image:: https://img.shields.io/pypi/status/openapi-schema-validator.svg | ||
:target: https://pypi.python.org/pypi/openapi-schema-validator | ||
|
||
About | ||
##### | ||
|
||
Openapi-schema-validator is a Python library that validates schema against the `OpenAPI Schema Specification v3.0 <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject>`__ which is an extended subset of the `JSON Schema Specification Wright Draft 00 <http://json-schema.org/>`__. | ||
|
||
Installation | ||
############ | ||
|
||
Recommended way (via pip): | ||
|
||
:: | ||
|
||
$ pip install openapi-schema-validator | ||
|
||
Alternatively you can download the code and install from the repository: | ||
|
||
.. code-block:: bash | ||
$ pip install -e git+https://github.com/p1c2u/openapi-schema-validator.git#egg=openapi_schema_validator | ||
Usage | ||
##### | ||
|
||
Simple usage | ||
|
||
.. code-block:: python | ||
from openapi_schema_validator import OAS30Validator, oas30_format_checker | ||
# A sample schema | ||
schema = { | ||
"type" : "object", | ||
"required": [ | ||
"name" | ||
], | ||
"properties": { | ||
"name": { | ||
"type": "string" | ||
}, | ||
"age": { | ||
"type": "integer", | ||
"format": "int32", | ||
"minimum": 0, | ||
"nullable": True, | ||
}, | ||
}, | ||
"additionalProperties": False, | ||
} | ||
validator = OAS30Validator(schema) | ||
# If no exception is raised by validate(), the instance is valid. | ||
validator.validate({"name": "John", "age": 23}) | ||
validator.validate({"name": "John", "city": "London"}) | ||
Traceback (most recent call last): | ||
... | ||
ValidationError: Additional properties are not allowed ('city' was unexpected) | ||
Related projects | ||
################ | ||
* `openapi-core <https://github.com/p1c2u/openapi-core>`__ | ||
* `openapi-spec-validator <https://github.com/p1c2u/openapi-spec-validator>`__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# -*- coding: utf-8 -*- | ||
from openapi_schema_validator._format import oas30_format_checker | ||
from openapi_schema_validator.validators import OAS30Validator | ||
|
||
__author__ = 'Artur Maciag' | ||
__email__ = 'maciag.artur@gmail.com' | ||
__version__ = '0.1.0' | ||
__url__ = 'https://github.com/p1c2u/openapi-schema-validator' | ||
__license__ = 'BSD 3-Clause License' | ||
|
||
__all__ = ['OAS30Validator', 'oas30_format_checker'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
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, | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
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 |
Oops, something went wrong.