Skip to content

Commit a2fc528

Browse files
committed
Get rid of object validator
1 parent b2410e2 commit a2fc528

File tree

9 files changed

+399
-434
lines changed

9 files changed

+399
-434
lines changed

openapi_core/schema/media_types/models.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ def unmarshal(self, value, custom_formatters=None, resolver=None):
5151
raise InvalidMediaTypeValue(exc)
5252

5353
try:
54-
unmarshalled = self.schema.unmarshal(value, custom_formatters=custom_formatters)
55-
except OpenAPISchemaError as exc:
56-
raise InvalidMediaTypeValue(exc)
57-
58-
try:
59-
return self.schema.obj_validate(
60-
unmarshalled, custom_formatters=custom_formatters)
54+
return self.schema.unmarshal(value, custom_formatters=custom_formatters)
6155
except OpenAPISchemaError as exc:
6256
raise InvalidMediaTypeValue(exc)

openapi_core/schema/parameters/models.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,10 @@ def unmarshal(self, value, custom_formatters=None, resolver=None):
123123
raise InvalidParameterValue(self.name, exc)
124124

125125
try:
126-
unmarshalled = self.schema.unmarshal(
126+
return self.schema.unmarshal(
127127
value,
128128
custom_formatters=custom_formatters,
129129
strict=True,
130130
)
131131
except OpenAPISchemaError as exc:
132132
raise InvalidParameterValue(self.name, exc)
133-
134-
try:
135-
return self.schema.obj_validate(
136-
unmarshalled, custom_formatters=custom_formatters)
137-
except OpenAPISchemaError as exc:
138-
raise InvalidParameterValue(self.name, exc)
+100-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,105 @@
1+
from base64 import b64encode, b64decode
2+
import binascii
3+
from datetime import datetime
4+
from uuid import UUID
5+
16
from jsonschema._format import FormatChecker
2-
from six import binary_type
7+
from jsonschema.exceptions import FormatError
8+
from six import binary_type, text_type, integer_types
9+
10+
11+
class StrictFormatChecker(FormatChecker):
12+
13+
def check(self, instance, format):
14+
if format not in self.checkers:
15+
raise FormatError(
16+
"Format checker for %r format not found" % (format, ))
17+
return super(StrictFormatChecker, self).check(
18+
instance, format)
19+
20+
21+
oas30_format_checker = StrictFormatChecker()
22+
23+
24+
@oas30_format_checker.checks('int32')
25+
def is_int32(instance):
26+
return isinstance(instance, integer_types)
27+
28+
29+
@oas30_format_checker.checks('int64')
30+
def is_int64(instance):
31+
return isinstance(instance, integer_types)
32+
33+
34+
@oas30_format_checker.checks('float')
35+
def is_float(instance):
36+
return isinstance(instance, float)
337

4-
oas30_format_checker = FormatChecker()
38+
39+
@oas30_format_checker.checks('double')
40+
def is_double(instance):
41+
# float has double precision in Python
42+
# It's double in CPython and Jython
43+
return isinstance(instance, float)
544

645

746
@oas30_format_checker.checks('binary')
8-
def binary(value):
9-
return isinstance(value, binary_type)
47+
def is_binary(instance):
48+
return isinstance(instance, binary_type)
49+
50+
51+
@oas30_format_checker.checks('byte', raises=(binascii.Error, TypeError))
52+
def is_byte(instance):
53+
if isinstance(instance, text_type):
54+
instance = instance.encode()
55+
56+
return b64encode(b64decode(instance)) == instance
57+
58+
59+
try:
60+
import strict_rfc3339
61+
except ImportError:
62+
try:
63+
import isodate
64+
except ImportError:
65+
pass
66+
else:
67+
@oas30_format_checker.checks(
68+
"date-time", raises=(ValueError, isodate.ISO8601Error))
69+
def is_datetime(instance):
70+
if isinstance(instance, binary_type):
71+
return False
72+
if not isinstance(instance, text_type):
73+
return True
74+
return isodate.parse_datetime(instance)
75+
else:
76+
@oas30_format_checker.checks("date-time")
77+
def is_datetime(instance):
78+
if isinstance(instance, binary_type):
79+
return False
80+
if not isinstance(instance, text_type):
81+
return True
82+
return strict_rfc3339.validate_rfc3339(instance)
83+
84+
85+
@oas30_format_checker.checks("date", raises=ValueError)
86+
def is_date(instance):
87+
if isinstance(instance, binary_type):
88+
return False
89+
if not isinstance(instance, text_type):
90+
return True
91+
return datetime.strptime(instance, "%Y-%m-%d")
92+
93+
94+
@oas30_format_checker.checks("uuid", raises=AttributeError)
95+
def is_uuid(instance):
96+
if isinstance(instance, binary_type):
97+
return False
98+
if not isinstance(instance, text_type):
99+
return True
100+
try:
101+
uuid_obj = UUID(instance)
102+
except ValueError:
103+
return False
104+
105+
return text_type(uuid_obj) == instance

openapi_core/schema/schemas/_validators.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from jsonschema.exceptions import ValidationError
1+
from jsonschema._utils import find_additional_properties, extras_msg
2+
from jsonschema.exceptions import ValidationError, FormatError
23

34

45
def type(validator, data_type, instance, schema):
@@ -9,6 +10,17 @@ def type(validator, data_type, instance, schema):
910
yield ValidationError("%r is not of type %s" % (instance, data_type))
1011

1112

13+
def format(validator, format, instance, schema):
14+
if instance is None:
15+
return
16+
17+
if validator.format_checker is not None:
18+
try:
19+
validator.format_checker.check(instance, format)
20+
except FormatError as error:
21+
yield ValidationError(error.message, cause=error.cause)
22+
23+
1224
def items(validator, items, instance, schema):
1325
if not validator.is_type(instance, "array"):
1426
return
@@ -23,5 +35,24 @@ def nullable(validator, is_nullable, instance, schema):
2335
yield ValidationError("None for not nullable")
2436

2537

38+
def additionalProperties(validator, aP, instance, schema):
39+
if not validator.is_type(instance, "object"):
40+
return
41+
42+
extras = set(find_additional_properties(instance, schema))
43+
44+
if not extras:
45+
return
46+
47+
if validator.is_type(aP, "object"):
48+
for extra in extras:
49+
for error in validator.descend(instance[extra], aP, path=extra):
50+
yield error
51+
elif validator.is_type(aP, "boolean"):
52+
if not aP:
53+
error = "Additional properties are not allowed (%s %s unexpected)"
54+
yield ValidationError(error % extras_msg(extras))
55+
56+
2657
def not_implemented(validator, value, instance, schema):
2758
pass

openapi_core/schema/schemas/factories.py

+59
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
"""OpenAPI core schemas factories module"""
22
import logging
33

4+
from six import iteritems
5+
46
from openapi_core.compat import lru_cache
57
from openapi_core.schema.properties.generators import PropertiesGenerator
68
from openapi_core.schema.schemas.models import Schema
9+
from openapi_core.schema.schemas.types import Contribution
710

811
log = logging.getLogger(__name__)
912

@@ -86,3 +89,59 @@ def properties_generator(self):
8689

8790
def _create_items(self, items_spec):
8891
return self.create(items_spec)
92+
93+
94+
class SchemaDictFactory(object):
95+
96+
contributions = (
97+
Contribution('type', src_prop_attr='value'),
98+
Contribution('format'),
99+
Contribution('properties', is_dict=True, dest_default={}),
100+
Contribution('required', dest_default=[]),
101+
Contribution('default'),
102+
Contribution('nullable', dest_default=False),
103+
Contribution('all_of', dest_prop_name='allOf', is_list=True, dest_default=[]),
104+
Contribution('one_of', dest_prop_name='oneOf', is_list=True, dest_default=[]),
105+
Contribution('additional_properties', dest_prop_name='additionalProperties', dest_default=True),
106+
Contribution('min_items', dest_prop_name='minItems'),
107+
Contribution('max_items', dest_prop_name='maxItems'),
108+
Contribution('min_length', dest_prop_name='minLength'),
109+
Contribution('max_length', dest_prop_name='maxLength'),
110+
Contribution('pattern', src_prop_attr='pattern'),
111+
Contribution('unique_items', dest_prop_name='uniqueItems', dest_default=False),
112+
Contribution('minimum'),
113+
Contribution('maximum'),
114+
Contribution('multiple_of', dest_prop_name='multipleOf'),
115+
Contribution('exclusive_minimum', dest_prop_name='exclusiveMinimum', dest_default=False),
116+
Contribution('exclusive_maximum', dest_prop_name='exclusiveMaximum', dest_default=False),
117+
Contribution('min_properties', dest_prop_name='minProperties'),
118+
Contribution('max_properties', dest_prop_name='maxProperties'),
119+
)
120+
121+
def create(self, schema):
122+
schema_dict = {}
123+
for contrib in self.contributions:
124+
self._contribute(schema, schema_dict, contrib)
125+
return schema_dict
126+
127+
def _contribute(self, schema, schema_dict, contrib):
128+
def src_map(x):
129+
return getattr(x, '__dict__')
130+
src_val = getattr(schema, contrib.src_prop_name)
131+
132+
if src_val and contrib.src_prop_attr:
133+
src_val = getattr(src_val, contrib.src_prop_attr)
134+
135+
if contrib.is_list:
136+
src_val = list(map(src_map, src_val))
137+
if contrib.is_dict:
138+
src_val = dict(
139+
(k, src_map(v))
140+
for k, v in iteritems(src_val)
141+
)
142+
143+
if src_val == contrib.dest_default:
144+
return
145+
146+
dest_prop_name = contrib.dest_prop_name or contrib.src_prop_name
147+
schema_dict[dest_prop_name] = src_val

0 commit comments

Comments
 (0)