Skip to content

Commit fa4169c

Browse files
committed
Response binary format support
1 parent ffb17a7 commit fa4169c

File tree

22 files changed

+80
-83
lines changed

22 files changed

+80
-83
lines changed

openapi_core/contrib/aiohttp/responses.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ def __init__(self, response: web.Response):
1313
self.response = response
1414

1515
@property
16-
def data(self) -> str:
16+
def data(self) -> bytes:
1717
if self.response.body is None:
18-
return ""
18+
return b""
1919
if isinstance(self.response.body, bytes):
20-
return self.response.body.decode("utf-8")
20+
return self.response.body
2121
assert isinstance(self.response.body, str)
22-
return self.response.body
22+
return self.response.body.encode("utf-8")
2323

2424
@property
2525
def status_code(self) -> int:

openapi_core/contrib/django/responses.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
"""OpenAPI core contrib django responses module"""
2+
from itertools import tee
3+
24
from django.http.response import HttpResponse
5+
from django.http.response import StreamingHttpResponse
36
from werkzeug.datastructures import Headers
47

58

69
class DjangoOpenAPIResponse:
710
def __init__(self, response: HttpResponse):
8-
if not isinstance(response, HttpResponse):
11+
if not isinstance(response, (HttpResponse, StreamingHttpResponse)):
912
raise TypeError(
10-
f"'response' argument is not type of {HttpResponse}"
13+
f"'response' argument is not type of {HttpResponse} or {StreamingHttpResponse}"
1114
)
1215
self.response = response
1316

1417
@property
15-
def data(self) -> str:
18+
def data(self) -> bytes:
19+
if isinstance(self.response, StreamingHttpResponse):
20+
resp_iter1, resp_iter2 = tee(self.response._iterator)
21+
self.response.streaming_content = resp_iter1
22+
content = b"".join(map(self.response.make_bytes, resp_iter2))
23+
return content
1624
assert isinstance(self.response.content, bytes)
17-
return self.response.content.decode("utf-8")
25+
return self.response.content
1826

1927
@property
2028
def status_code(self) -> int:

openapi_core/contrib/falcon/responses.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""OpenAPI core contrib falcon responses module"""
2+
from itertools import tee
3+
24
from falcon.response import Response
35
from werkzeug.datastructures import Headers
46

@@ -10,11 +12,16 @@ def __init__(self, response: Response):
1012
self.response = response
1113

1214
@property
13-
def data(self) -> str:
15+
def data(self) -> bytes:
1416
if self.response.text is None:
15-
return ""
17+
if self.response.stream is None:
18+
return b""
19+
resp_iter1, resp_iter2 = tee(self.response.stream)
20+
self.response.stream = resp_iter1
21+
content = b"".join(resp_iter2)
22+
return content
1623
assert isinstance(self.response.text, str)
17-
return self.response.text
24+
return self.response.text.encode("utf-8")
1825

1926
@property
2027
def status_code(self) -> int:

openapi_core/contrib/requests/responses.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ def __init__(self, response: Response):
1010
self.response = response
1111

1212
@property
13-
def data(self) -> str:
13+
def data(self) -> bytes:
1414
assert isinstance(self.response.content, bytes)
15-
return self.response.content.decode("utf-8")
15+
return self.response.content
1616

1717
@property
1818
def status_code(self) -> int:

openapi_core/contrib/starlette/responses.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ def __init__(self, response: Response):
1010
self.response = response
1111

1212
@property
13-
def data(self) -> str:
13+
def data(self) -> bytes:
1414
if isinstance(self.response.body, bytes):
15-
return self.response.body.decode("utf-8")
15+
return self.response.body
1616
assert isinstance(self.response.body, str)
17-
return self.response.body
17+
return self.response.body.encode("utf-8")
1818

1919
@property
2020
def status_code(self) -> int:

openapi_core/contrib/werkzeug/responses.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""OpenAPI core contrib werkzeug responses module"""
2+
from itertools import tee
3+
24
from werkzeug.datastructures import Headers
35
from werkzeug.wrappers import Response
46

@@ -10,8 +12,12 @@ def __init__(self, response: Response):
1012
self.response = response
1113

1214
@property
13-
def data(self) -> str:
14-
return self.response.get_data(as_text=True)
15+
def data(self) -> bytes:
16+
if not self.response.is_sequence:
17+
resp_iter1, resp_iter2 = tee(self.response.iter_encoded())
18+
self.response.response = resp_iter1
19+
return b"".join(resp_iter2)
20+
return self.response.get_data(as_text=False)
1521

1622
@property
1723
def status_code(self) -> int:

openapi_core/protocols.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class Response(Protocol):
120120
"""
121121

122122
@property
123-
def data(self) -> str:
123+
def data(self) -> Optional[bytes]:
124124
...
125125

126126
@property

openapi_core/testing/responses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class MockResponse:
1010
def __init__(
1111
self,
12-
data: str,
12+
data: bytes,
1313
status_code: int = 200,
1414
headers: Optional[Dict[str, Any]] = None,
1515
content_type: str = "application/json",

tests/integration/contrib/aiohttp/test_aiohttp_project.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ def api_key_encoded(self):
3838

3939

4040
class TestPetPhotoView(BaseTestPetstore):
41-
@pytest.mark.xfail(
42-
reason="response binary format not supported",
43-
strict=True,
44-
)
4541
async def test_get_valid(self, client, data_gif):
4642
headers = {
4743
"Authorization": "Basic testuser",

tests/integration/contrib/django/test_django_project.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,6 @@ def test_get_skip_response_validation(self, client):
398398

399399

400400
class TestPetPhotoView(BaseTestDjangoProject):
401-
@pytest.mark.xfail(
402-
reason="response binary format not supported",
403-
strict=True,
404-
)
405401
def test_get_valid(self, client, data_gif):
406402
headers = {
407403
"HTTP_AUTHORIZATION": "Basic testuser",

0 commit comments

Comments
 (0)