Skip to content

Commit 8a99a63

Browse files
authored
Merge pull request #348 from p1c2u/refactor/casting-refactor
Casting refactor
2 parents b4cff7b + 8b05328 commit 8a99a63

File tree

7 files changed

+84
-23
lines changed

7 files changed

+84
-23
lines changed
Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,55 @@
11
from openapi_core.casting.schemas.exceptions import CastError
22

33

4-
class PrimitiveCaster:
4+
class BaseSchemaCaster:
55

6-
def __init__(self, schema, caster_callable):
6+
def __init__(self, schema):
77
self.schema = schema
8-
self.caster_callable = caster_callable
98

109
def __call__(self, value):
1110
if value is None:
1211
return value
12+
13+
return self.cast(value)
14+
15+
def cast(self, value):
16+
raise NotImplementedError
17+
18+
19+
class CallableSchemaCaster(BaseSchemaCaster):
20+
21+
def __init__(self, schema, caster_callable):
22+
super().__init__(schema)
23+
self.caster_callable = caster_callable
24+
25+
def cast(self, value):
1326
try:
1427
return self.caster_callable(value)
1528
except (ValueError, TypeError):
1629
raise CastError(value, self.schema['type'])
1730

1831

19-
class DummyCaster:
32+
class DummyCaster(BaseSchemaCaster):
2033

21-
def __call__(self, value):
34+
def cast(self, value):
2235
return value
2336

2437

25-
class ArrayCaster:
38+
class ComplexCaster(BaseSchemaCaster):
2639

2740
def __init__(self, schema, casters_factory):
28-
self.schema = schema
41+
super().__init__(schema)
2942
self.casters_factory = casters_factory
3043

44+
45+
class ArrayCaster(ComplexCaster):
46+
3147
@property
3248
def items_caster(self):
3349
return self.casters_factory.create(self.schema / 'items')
3450

35-
def __call__(self, value):
36-
if value is None:
37-
return value
38-
return list(map(self.items_caster, value))
51+
def cast(self, value):
52+
try:
53+
return list(map(self.items_caster, value))
54+
except (ValueError, TypeError):
55+
raise CastError(value, self.schema['type'])

openapi_core/casting/schemas/exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ class CastError(OpenAPIError):
1010
type: str
1111

1212
def __str__(self):
13-
return "Failed to cast value {value} to type {type}".format(
14-
value=self.value, type=self.type)
13+
return "Failed to cast value to {type} type: {value}".format(
14+
type=self.type, value=self.value)

openapi_core/casting/schemas/factories.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from openapi_core.casting.schemas.casters import (
2-
PrimitiveCaster, DummyCaster, ArrayCaster
2+
ArrayCaster, CallableSchemaCaster, DummyCaster,
33
)
44
from openapi_core.casting.schemas.util import forcebool
55

@@ -20,11 +20,12 @@ class SchemaCastersFactory:
2020

2121
def create(self, schema):
2222
schema_type = schema.getkey('type', 'any')
23+
2324
if schema_type in self.DUMMY_CASTERS:
24-
return DummyCaster()
25-
elif schema_type in self.PRIMITIVE_CASTERS:
25+
return DummyCaster(schema)
26+
27+
if schema_type in self.PRIMITIVE_CASTERS:
2628
caster_callable = self.PRIMITIVE_CASTERS[schema_type]
27-
return PrimitiveCaster(schema, caster_callable)
28-
elif schema_type in self.COMPLEX_CASTERS:
29-
caster_class = self.COMPLEX_CASTERS[schema_type]
30-
return caster_class(schema, self)
29+
return CallableSchemaCaster(schema, caster_callable)
30+
31+
return ArrayCaster(schema, self)

tests/integration/contrib/falcon/test_falcon_middlewares.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ def test_endpoint_error(self, client):
173173
),
174174
'status': 400,
175175
'title': (
176-
"Failed to cast value invalidparameter to type integer"
176+
"Failed to cast value to integer type: "
177+
"invalidparameter"
177178
)
178179
}
179180
]

tests/integration/contrib/flask/test_flask_decorator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ def test_endpoint_error(self, client):
159159
),
160160
'status': 400,
161161
'title': (
162-
"Failed to cast value invalidparameter to type integer"
162+
"Failed to cast value to integer type: "
163+
"invalidparameter"
163164
)
164165
}
165166
]

tests/integration/contrib/flask/test_flask_views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ def test_endpoint_error(self, client):
151151
),
152152
'status': 400,
153153
'title': (
154-
"Failed to cast value invalidparameter to type integer"
154+
"Failed to cast value to integer type: "
155+
"invalidparameter"
155156
)
156157
}
157158
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import pytest
2+
3+
from openapi_core.casting.schemas.exceptions import CastError
4+
from openapi_core.casting.schemas.factories import SchemaCastersFactory
5+
from openapi_core.spec.paths import SpecPath
6+
7+
8+
class TestSchemaCaster:
9+
10+
@pytest.fixture
11+
def caster_factory(self):
12+
def create_caster(schema):
13+
return SchemaCastersFactory().create(schema)
14+
return create_caster
15+
16+
def test_array_invalid_type(self, caster_factory):
17+
spec = {
18+
'type': 'array',
19+
'items': {
20+
'type': 'number',
21+
},
22+
}
23+
schema = SpecPath.from_spec(spec)
24+
value = ['test', 'test2']
25+
26+
with pytest.raises(CastError):
27+
caster_factory(schema)(value)
28+
29+
def test_array_invalid_value(self, caster_factory):
30+
spec = {
31+
'type': 'array',
32+
'items': {
33+
'type': 'number',
34+
},
35+
}
36+
schema = SpecPath.from_spec(spec)
37+
value = 3.14
38+
39+
with pytest.raises(CastError):
40+
caster_factory(schema)(value)

0 commit comments

Comments
 (0)