Skip to content

Commit 021de64

Browse files
committed
Typed style deserializers
1 parent 33e4503 commit 021de64

File tree

20 files changed

+406
-103
lines changed

20 files changed

+406
-103
lines changed

openapi_core/casting/schemas/exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from dataclasses import dataclass
22
from typing import Any
33

4-
from openapi_core.exceptions import OpenAPIError
4+
from openapi_core.deserializing.exceptions import DeserializeError
55

66

77
@dataclass
8-
class CastError(OpenAPIError):
8+
class CastError(DeserializeError):
99
"""Schema cast operation error"""
1010

1111
value: Any

openapi_core/deserializing/media_types/__init__.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
from openapi_core.deserializing.media_types.util import plain_loads
1313
from openapi_core.deserializing.media_types.util import urlencoded_form_loads
1414
from openapi_core.deserializing.media_types.util import xml_loads
15-
from openapi_core.deserializing.styles import style_deserializers_factory
1615

17-
__all__ = ["media_type_deserializers_factory"]
16+
__all__ = ["media_type_deserializers", "MediaTypeDeserializersFactory"]
1817

1918
media_type_deserializers: MediaTypeDeserializersDict = defaultdict(
2019
lambda: binary_loads,
@@ -30,8 +29,3 @@
3029
"multipart/form-data": data_form_loads,
3130
}
3231
)
33-
34-
media_type_deserializers_factory = MediaTypeDeserializersFactory(
35-
style_deserializers_factory,
36-
media_type_deserializers=media_type_deserializers,
37-
)

openapi_core/deserializing/media_types/factories.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from openapi_core.deserializing.media_types.deserializers import (
1313
MediaTypesDeserializer,
1414
)
15+
from openapi_core.deserializing.styles.datatypes import StyleDeserializersDict
1516
from openapi_core.deserializing.styles.factories import (
1617
StyleDeserializersFactory,
1718
)
@@ -28,6 +29,31 @@ def __init__(
2829
media_type_deserializers = {}
2930
self.media_type_deserializers = media_type_deserializers
3031

32+
@classmethod
33+
def from_schema_casters_factory(
34+
cls,
35+
schema_casters_factory: StyleDeserializersFactory,
36+
style_deserializers: StyleDeserializersDict = None,
37+
media_type_deserializers: Optional[MediaTypeDeserializersDict] = None,
38+
) -> "MediaTypeDeserializersFactory":
39+
from openapi_core.deserializing.media_types import (
40+
media_type_deserializers as default_media_type_deserializers,
41+
)
42+
from openapi_core.deserializing.styles import (
43+
style_deserializers as default_style_deserializers,
44+
)
45+
46+
style_deserializers_factory = StyleDeserializersFactory(
47+
schema_casters_factory,
48+
style_deserializers=style_deserializers
49+
or default_style_deserializers,
50+
)
51+
return cls(
52+
style_deserializers_factory,
53+
media_type_deserializers=media_type_deserializers
54+
or default_media_type_deserializers,
55+
)
56+
3157
def create(
3258
self,
3359
mimetype: str,

openapi_core/deserializing/styles/__init__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from openapi_core.deserializing.styles.util import simple_loads
1111
from openapi_core.deserializing.styles.util import space_delimited_loads
1212

13-
__all__ = ["style_deserializers_factory"]
13+
__all__ = ["style_deserializers", "StyleDeserializersFactory"]
1414

1515
style_deserializers: StyleDeserializersDict = {
1616
"matrix": matrix_loads,
@@ -21,7 +21,3 @@
2121
"pipeDelimited": pipe_delimited_loads,
2222
"deepObject": deep_object_loads,
2323
}
24-
25-
style_deserializers_factory = StyleDeserializersFactory(
26-
style_deserializers=style_deserializers,
27-
)

openapi_core/deserializing/styles/datatypes.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@
33
from typing import Dict
44
from typing import Mapping
55

6-
DeserializerCallable = Callable[[bool, str, str, Mapping[str, Any]], Any]
6+
from jsonschema_path import SchemaPath
7+
8+
DeserializerCallable = Callable[
9+
[bool, str, SchemaPath, Mapping[str, Any], bool], Any
10+
]
711
StyleDeserializersDict = Dict[str, DeserializerCallable]

openapi_core/deserializing/styles/deserializers.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from typing import Mapping
44
from typing import Optional
55

6+
from jsonschema_path import SchemaPath
7+
8+
from openapi_core.casting.schemas.casters import SchemaCaster
9+
from openapi_core.casting.schemas.exceptions import CastError
610
from openapi_core.deserializing.exceptions import DeserializeError
711
from openapi_core.deserializing.styles.datatypes import DeserializerCallable
812

@@ -13,13 +17,16 @@ def __init__(
1317
style: str,
1418
explode: bool,
1519
name: str,
16-
schema_type: str,
20+
schema: SchemaPath,
21+
caster: SchemaCaster,
1722
deserializer_callable: Optional[DeserializerCallable] = None,
1823
):
1924
self.style = style
2025
self.explode = explode
2126
self.name = name
22-
self.schema_type = schema_type
27+
self.schema = schema
28+
self.schema_type = schema.getkey("type", "")
29+
self.caster = caster
2330
self.deserializer_callable = deserializer_callable
2431

2532
def deserialize(self, location: Mapping[str, Any]) -> Any:
@@ -28,8 +35,13 @@ def deserialize(self, location: Mapping[str, Any]) -> Any:
2835
return location[self.name]
2936

3037
try:
31-
return self.deserializer_callable(
38+
value = self.deserializer_callable(
3239
self.explode, self.name, self.schema_type, location
3340
)
34-
except (ValueError, TypeError, AttributeError):
35-
raise DeserializeError(self.style, self.name)
41+
except (ValueError, TypeError, AttributeError) as exc:
42+
raise DeserializeError(self.style, self.name) from exc
43+
44+
try:
45+
return self.caster.cast(value)
46+
except (ValueError, TypeError, AttributeError) as exc:
47+
raise CastError(value, self.schema_type) from exc

openapi_core/deserializing/styles/factories.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
from jsonschema_path import SchemaPath
44

5+
from openapi_core.casting.schemas.factories import SchemaCastersFactory
56
from openapi_core.deserializing.styles.datatypes import StyleDeserializersDict
67
from openapi_core.deserializing.styles.deserializers import StyleDeserializer
78

89

910
class StyleDeserializersFactory:
1011
def __init__(
1112
self,
13+
schema_casters_factory: SchemaCastersFactory,
1214
style_deserializers: Optional[StyleDeserializersDict] = None,
1315
):
16+
self.schema_casters_factory = schema_casters_factory
1417
if style_deserializers is None:
1518
style_deserializers = {}
1619
self.style_deserializers = style_deserializers
@@ -22,9 +25,8 @@ def create(
2225
schema: SchemaPath,
2326
name: str,
2427
) -> StyleDeserializer:
25-
schema_type = schema.getkey("type", "")
26-
2728
deserialize_callable = self.style_deserializers.get(style)
29+
caster = self.schema_casters_factory.create(schema)
2830
return StyleDeserializer(
29-
style, explode, name, schema_type, deserialize_callable
31+
style, explode, name, schema, caster, deserialize_callable
3032
)

openapi_core/unmarshalling/request/protocols.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,12 @@
88
from openapi_spec_validator.validation.types import SpecValidatorType
99

1010
from openapi_core.casting.schemas.factories import SchemaCastersFactory
11-
from openapi_core.deserializing.media_types import (
12-
media_type_deserializers_factory,
13-
)
1411
from openapi_core.deserializing.media_types.datatypes import (
1512
MediaTypeDeserializersDict,
1613
)
1714
from openapi_core.deserializing.media_types.factories import (
1815
MediaTypeDeserializersFactory,
1916
)
20-
from openapi_core.deserializing.styles import style_deserializers_factory
2117
from openapi_core.deserializing.styles.factories import (
2218
StyleDeserializersFactory,
2319
)
@@ -43,8 +39,8 @@ def __init__(
4339
self,
4440
spec: SchemaPath,
4541
base_url: Optional[str] = None,
46-
style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory,
47-
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
42+
style_deserializers_factory: StyleDeserializersFactory = None,
43+
media_type_deserializers_factory: MediaTypeDeserializersFactory = None,
4844
schema_casters_factory: Optional[SchemaCastersFactory] = None,
4945
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
5046
path_finder_cls: Optional[PathFinderType] = None,
@@ -74,8 +70,12 @@ def __init__(
7470
self,
7571
spec: SchemaPath,
7672
base_url: Optional[str] = None,
77-
style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory,
78-
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
73+
style_deserializers_factory: Optional[
74+
StyleDeserializersFactory
75+
] = None,
76+
media_type_deserializers_factory: Optional[
77+
MediaTypeDeserializersFactory
78+
] = None,
7979
schema_casters_factory: Optional[SchemaCastersFactory] = None,
8080
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
8181
path_finder_cls: Optional[PathFinderType] = None,

openapi_core/unmarshalling/request/unmarshallers.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,12 @@
44
from openapi_spec_validator.validation.types import SpecValidatorType
55

66
from openapi_core.casting.schemas.factories import SchemaCastersFactory
7-
from openapi_core.deserializing.media_types import (
8-
media_type_deserializers_factory,
9-
)
107
from openapi_core.deserializing.media_types.datatypes import (
118
MediaTypeDeserializersDict,
129
)
1310
from openapi_core.deserializing.media_types.factories import (
1411
MediaTypeDeserializersFactory,
1512
)
16-
from openapi_core.deserializing.styles import style_deserializers_factory
1713
from openapi_core.deserializing.styles.factories import (
1814
StyleDeserializersFactory,
1915
)
@@ -85,8 +81,12 @@ def __init__(
8581
self,
8682
spec: SchemaPath,
8783
base_url: Optional[str] = None,
88-
style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory,
89-
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
84+
style_deserializers_factory: Optional[
85+
StyleDeserializersFactory
86+
] = None,
87+
media_type_deserializers_factory: Optional[
88+
MediaTypeDeserializersFactory
89+
] = None,
9090
schema_casters_factory: Optional[SchemaCastersFactory] = None,
9191
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
9292
path_finder_cls: Optional[PathFinderType] = None,

openapi_core/unmarshalling/response/protocols.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,12 @@
88
from openapi_spec_validator.validation.types import SpecValidatorType
99

1010
from openapi_core.casting.schemas.factories import SchemaCastersFactory
11-
from openapi_core.deserializing.media_types import (
12-
media_type_deserializers_factory,
13-
)
1411
from openapi_core.deserializing.media_types.datatypes import (
1512
MediaTypeDeserializersDict,
1613
)
1714
from openapi_core.deserializing.media_types.factories import (
1815
MediaTypeDeserializersFactory,
1916
)
20-
from openapi_core.deserializing.styles import style_deserializers_factory
2117
from openapi_core.deserializing.styles.factories import (
2218
StyleDeserializersFactory,
2319
)
@@ -44,8 +40,12 @@ def __init__(
4440
self,
4541
spec: SchemaPath,
4642
base_url: Optional[str] = None,
47-
style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory,
48-
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
43+
style_deserializers_factory: Optional[
44+
StyleDeserializersFactory
45+
] = None,
46+
media_type_deserializers_factory: Optional[
47+
MediaTypeDeserializersFactory
48+
] = None,
4949
schema_casters_factory: Optional[SchemaCastersFactory] = None,
5050
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
5151
path_finder_cls: Optional[PathFinderType] = None,
@@ -75,8 +75,8 @@ def __init__(
7575
self,
7676
spec: SchemaPath,
7777
base_url: Optional[str] = None,
78-
style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory,
79-
media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory,
78+
style_deserializers_factory: StyleDeserializersFactory = None,
79+
media_type_deserializers_factory: MediaTypeDeserializersFactory = None,
8080
schema_casters_factory: Optional[SchemaCastersFactory] = None,
8181
schema_validators_factory: Optional[SchemaValidatorsFactory] = None,
8282
path_finder_cls: Optional[PathFinderType] = None,

0 commit comments

Comments
 (0)