Skip to content

Commit bc1207e

Browse files
committed
More media types supported
1 parent efdc5be commit bc1207e

File tree

7 files changed

+53
-12
lines changed

7 files changed

+53
-12
lines changed

Diff for: docs/customizations.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ If you know you have a valid specification already, disabling the validator can
2121
Media type deserializers
2222
------------------------
2323

24-
Pass custom defined media type deserializers dictionary with supported mimetypes as a key to `unmarshal_response` function:
24+
OpenAPI comes with a set of built-in media type deserializers such as: ``application/json``, ``application/xml``, ``application/x-www-form-urlencoded`` or ``multipart/form-data``.
25+
26+
You can also define your own ones. Pass custom defined media type deserializers dictionary with supported mimetypes as a key to `unmarshal_response` function:
2527

2628
.. code-block:: python
2729
:emphasize-lines: 13

Diff for: openapi_core/deserializing/media_types/__init__.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from json import loads
1+
from json import loads as json_loads
2+
from xml.etree.ElementTree import fromstring as xml_loads
23

34
from openapi_core.deserializing.media_types.datatypes import (
45
MediaTypeDeserializersDict,
@@ -7,12 +8,18 @@
78
MediaTypeDeserializersFactory,
89
)
910
from openapi_core.deserializing.media_types.util import data_form_loads
11+
from openapi_core.deserializing.media_types.util import plain_loads
1012
from openapi_core.deserializing.media_types.util import urlencoded_form_loads
1113

1214
__all__ = ["media_type_deserializers_factory"]
1315

1416
media_type_deserializers: MediaTypeDeserializersDict = {
15-
"application/json": loads,
17+
"text/html": plain_loads,
18+
"text/plain": plain_loads,
19+
"application/json": json_loads,
20+
"application/vnd.api+json": json_loads,
21+
"application/xml": xml_loads,
22+
"application/xhtml+xml": xml_loads,
1623
"application/x-www-form-urlencoded": urlencoded_form_loads,
1724
"multipart/form-data": data_form_loads,
1825
}

Diff for: openapi_core/deserializing/media_types/deserializers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import warnings
22
from typing import Any
33
from typing import Optional
4+
from xml.etree.ElementTree import ParseError
45

56
from openapi_core.deserializing.media_types.datatypes import (
67
DeserializerCallable,
@@ -26,5 +27,5 @@ def deserialize(self, value: Any) -> Any:
2627

2728
try:
2829
return self.deserializer_callable(value)
29-
except (ValueError, TypeError, AttributeError):
30+
except (ParseError, ValueError, TypeError, AttributeError):
3031
raise MediaTypeDeserializeError(self.mimetype, value)

Diff for: openapi_core/deserializing/media_types/util.py

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
from urllib.parse import parse_qsl
66

77

8+
def plain_loads(value: Union[str, bytes]) -> str:
9+
if isinstance(value, bytes):
10+
value = value.decode("ASCII", errors="surrogateescape")
11+
return value
12+
13+
814
def urlencoded_form_loads(value: Any) -> Dict[str, Any]:
915
return dict(parse_qsl(value))
1016

Diff for: tests/integration/test_petstore.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,7 @@ def test_get_pets_response_no_schema(self, spec):
233233
data = "<html></html>"
234234
response = MockResponse(data, status_code=404, mimetype="text/html")
235235

236-
with pytest.warns(UserWarning):
237-
response_result = unmarshal_response(request, response, spec=spec)
236+
response_result = unmarshal_response(request, response, spec=spec)
238237

239238
assert response_result.errors == []
240239
assert response_result.data == data

Diff for: tests/integration/unmarshalling/test_request_unmarshaller.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,7 @@ def test_post_pets_plain_no_schema(self, request_unmarshaller):
352352
mimetype="text/plain",
353353
)
354354

355-
with pytest.warns(UserWarning):
356-
result = request_unmarshaller.unmarshal(request)
355+
result = request_unmarshaller.unmarshal(request)
357356

358357
assert result.errors == []
359358
assert result.parameters == Parameters(

Diff for: tests/unit/deserializing/test_media_types_deserializers.py

+31-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from xml.etree.ElementTree import Element
2+
13
import pytest
24

35
from openapi_core.deserializing.exceptions import DeserializeError
@@ -46,23 +48,48 @@ def test_no_deserializer(self, deserializer_factory):
4648

4749
assert result == value
4850

49-
def test_json_empty(self, deserializer_factory):
50-
mimetype = "application/json"
51+
@pytest.mark.parametrize("mimetype", [
52+
"application/json", "application/vnd.api+json",
53+
])
54+
def test_json_empty(self, deserializer_factory, mimetype):
5155
deserializer = deserializer_factory(mimetype)
5256
value = ""
5357

5458
with pytest.raises(DeserializeError):
5559
deserializer.deserialize(value)
5660

57-
def test_json_empty_object(self, deserializer_factory):
58-
mimetype = "application/json"
61+
@pytest.mark.parametrize("mimetype", [
62+
"application/json", "application/vnd.api+json",
63+
])
64+
def test_json_empty_object(self, deserializer_factory, mimetype):
5965
deserializer = deserializer_factory(mimetype)
6066
value = "{}"
6167

6268
result = deserializer.deserialize(value)
6369

6470
assert result == {}
6571

72+
@pytest.mark.parametrize("mimetype", [
73+
"application/xml", "application/xhtml+xml",
74+
])
75+
def test_xml_empty(self, deserializer_factory, mimetype):
76+
deserializer = deserializer_factory(mimetype)
77+
value = ""
78+
79+
with pytest.raises(DeserializeError):
80+
deserializer.deserialize(value)
81+
82+
@pytest.mark.parametrize("mimetype", [
83+
"application/xml", "application/xhtml+xml",
84+
])
85+
def test_xml_valid(self, deserializer_factory, mimetype):
86+
deserializer = deserializer_factory(mimetype)
87+
value = "<obj>text</obj>"
88+
89+
result = deserializer.deserialize(value)
90+
91+
assert type(result) is Element
92+
6693
def test_urlencoded_form_empty(self, deserializer_factory):
6794
mimetype = "application/x-www-form-urlencoded"
6895
deserializer = deserializer_factory(mimetype)

0 commit comments

Comments
 (0)