8000 Merge pull request #710 from python-openapi/feature/request-response-… · python-openapi/openapi-core@09f065b · GitHub
[go: up one dir, main page]

Skip to content

Commit 09f065b

Browse files
authored
Merge pull request #710 from python-openapi/feature/request-response-binary-format-support
Request response binary format support
2 parents 16e7b1a + 10fdbea commit 09f065b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+239
-221
lines changed

openapi_core/contrib/aiohttp/requests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class Empty:
1919
class AIOHTTPOpenAPIWebRequest:
2020
__slots__ = ("request", "parameters", "_get_body", "_body")
2121

22-
def __init__(self, request: web.Request, *, body: str | None):
22+
def __init__(self, request: web.Request, *, body: bytes | None):
2323
if not isinstance(request, web.Request):
2424
raise TypeError(
2525
f"'request' argument is not type of {web.Request.__qualname__!r}"
@@ -45,7 +45,7 @@ def method(self) -> str:
4545
return self.request.method.lower()
4646

4747
@property
48-
def body(self) -> str | None:
48+
def body(self) -> bytes | None:
4949
return self._body
5050

5151
@property

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/requests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ def method(self) -> str:
7676
return self.request.method.lower()
7777

7878
@property
79-
def body(self) -> str:
79+
def body(self) -> bytes:
8080
assert isinstance(self.request.body, bytes)
81-
return self.request.body.decode("utf-8")
81+
return self.request.body
8282

8383
@property
8484
def content_type(self) -> str:

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/requests.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@ def method(self) -> str:
4949
return self.request.method.lower()
5050

5151
@property
52-
def body(self) -> Optional[str]:
52+
def body(self) -> Optional[bytes]:
53+
# Falcon doesn't store raw request stream.
54+
# That's why we need to revert deserialized data
55+
5356
# Support falcon-jsonify.
5457
if hasattr(self.request, "json"):
55-
return dumps(self.request.json)
58+
return dumps(self.request.json).encode("utf-8")
5659

57-
# Falcon doesn't store raw request stream.
58-
# That's why we need to revert serialized data
5960
media = self.request.get_media(
6061
default_when_empty=self.default_when_empty,
6162
)
@@ -74,7 +75,7 @@ def body(self) -> Optional[str]:
7475
return None
7576
else:
7677
assert isinstance(body, bytes)
77-
return body.decode("utf-8")
78+
return body
7879

7980
@property
8081
def content_type(self) -> str:

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/requests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ def method(self) -> str:
6464
return method and method.lower() or ""
6565

6666
@property
67-
def body(self) -> Optional[str]:
67+
def body(self) -> Optional[bytes]:
6868
if self.request.body is None:
6969
return None
7070
if isinstance(self.request.body, bytes):
71-
return self.request.body.decode("utf-8")
71+
return self.request.body
7272
assert isinstance(self.request.body, str)
7373
# TODO: figure out if request._body_position is relevant
74-
return self.request.body
74+
return self.request.body.encode("utf-8")
7575

7676
@property
7777
def content_type(self) -> str:

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/requests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ def method(self) -> str:
3434
return self.request.method.lower()
3535

3636
@property
37-
def body(self) -> Optional[str]:
37+
def body(self) -> Optional[bytes]:
3838
body = self._get_body()
3939
if body is None:
4040
return None
4141
if isinstance(body, bytes):
42-
return body.decode("utf-8")
42+
return body
4343
assert isinstance(body, str)
44-
return body
44+
return body.encode("utf-8")
4545

4646
@property
4747
def content_type(self) -> str:

openapi_core/contrib/starlette/responses.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
"""OpenAPI core contrib starlette responses module"""
2+
from typing import Optional
3+
24
from starlette.datastructures import Headers
35
from starlette.responses import Response
6+
from starlette.responses import StreamingResponse
47

58

69
class StarletteOpenAPIResponse:
7-
def __init__(self, response: Response):
10+
def __init__(self, response: Response, data: Optional[bytes] = None):
811
if not isinstance(response, Response):
912
raise TypeError(f"'response' argument is not type of {Response}")
1013
self.response = response
1114

15+
if data is None and isinstance(response, StreamingResponse):
16+
raise RuntimeError(
17+
f"'data' argument is required for {StreamingResponse}"
18+
)
19+
self._data = data
20+
1221
@property
13-
def data(self) -> str:
22+
def data(self) -> bytes:
23+
if self._data is not None:
24+
return self._data
1425
if isinstance(self.response.body, bytes):
15-
return self.response.body.decode("utf-8")
26+
return self.response.body
1627
assert isinstance(self.response.body, str)
17-
return self.response.body
28+
return self.response.body.encode("utf-8")
1829

1930
@property
2031
def status_code(self) -> int:

0 commit comments

Comments
 (0)
0