Skip to content

Commit 87b969c

Browse files
dbantyfdintino
andauthored
Support all text/* content types in responses (#897)
Replaces #780. Closes #797, #821 TODO: - [x] Fix unnecessary cast --------- Co-authored-by: Frankie Dintino <[email protected]> Co-authored-by: Dylan Anthony <[email protected]>
1 parent ea4b545 commit 87b969c

File tree

15 files changed

+356
-70
lines changed

15 files changed

+356
-70
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
default: minor
3+
---
4+
5+
# Support all `text/*` content types in responses
6+
7+
Within an API response, any content type which starts with `text/` will now be treated the same as `text/html` already was—they will return the `response.text` attribute from the [httpx Response](https://www.python-httpx.org/api/#response).
8+
9+
Thanks to @fdintino for the initial implementation, and thanks for the discussions from @kairntech, @rubenfiszel, and @antoneladestito.
10+
11+
Closes #797 and #821.

end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/responses/__init__.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import types
44

5-
from . import post_responses_unions_simple_before_complex
5+
from . import post_responses_unions_simple_before_complex, text_response
66

77

88
class ResponsesEndpoints:
@@ -12,3 +12,10 @@ def post_responses_unions_simple_before_complex(cls) -> types.ModuleType:
1212
Regression test for #603
1313
"""
1414
return post_responses_unions_simple_before_complex
15+
16+
@classmethod
17+
def text_response(cls) -> types.ModuleType:
18+
"""
19+
Text Response
20+
"""
21+
return text_response
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from http import HTTPStatus
2+
from typing import Any, Dict, Optional, Union
3+
4+
import httpx
5+
6+
from ... import errors
7+
from ...client import AuthenticatedClient, Client
8+
from ...types import Response
9+
10+
11+
def _get_kwargs() -> Dict[str, Any]:
12+
return {
13+
"method": "post",
14+
"url": "/responses/text",
15+
}
16+
17+
18+
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[str]:
19+
if response.status_code == HTTPStatus.OK:
20+
response_200 = response.text
21+
return response_200
22+
if client.raise_on_unexpected_status:
23+
raise errors.UnexpectedStatus(response.status_code, response.content)
24+
else:
25+
return None
26+
27+
28+
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[str]:
29+
return Response(
30+
status_code=HTTPStatus(response.status_code),
31+
content=response.content,
32+
headers=response.headers,
33+
parsed=_parse_response(client=client, response=response),
34+
)
35+
36+
37+
def sync_detailed(
38+
*,
39+
client: Union[AuthenticatedClient, Client],
40+
) -> Response[str]:
41+
"""Text Response
42+
43+
Raises:
44+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
45+
httpx.TimeoutException: If the request takes longer than Client.timeout.
46+
47+
Returns:
48+
Response[str]
49+
"""
50+
51+
kwargs = _get_kwargs()
52+
53+
response = client.get_httpx_client().request(
54+
**kwargs,
55+
)
56+
57+
return _build_response(client=client, response=response)
58+
59+
60+
def sync(
61+
*,
62+
client: Union[AuthenticatedClient, Client],
63+
) -> Optional[str]:
64+
"""Text Response
65+
66+
Raises:
67+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
68+
httpx.TimeoutException: If the request takes longer than Client.timeout.
69+
70+
Returns:
71+
str
72+
"""
73+
74+
return sync_detailed(
75+
client=client,
76+
).parsed
77+
78+
79+
async def asyncio_detailed(
80+
*,
81+
client: Union[AuthenticatedClient, Client],
82+
) -> Response[str]:
83+
"""Text Response
84+
85+
Raises:
86+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
87+
httpx.TimeoutException: If the request takes longer than Client.timeout.
88+
89+
Returns:
90+
Response[str]
91+
"""
92+
93+
kwargs = _get_kwargs()
94+
95+
response = await client.get_async_httpx_client().request(**kwargs)
96+
97+
return _build_response(client=client, response=response)
98+
99+
100+
async def asyncio(
101+
*,
102+
client: Union[AuthenticatedClient, Client],
103+
) -> Optional[str]:
104+
"""Text Response
105+
106+
Raises:
107+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
108+
httpx.TimeoutException: If the request takes longer than Client.timeout.
109+
110+
Returns:
111+
str
112+
"""
113+
114+
return (
115+
await asyncio_detailed(
116+
client=client,
117+
)
118+
).parsed

end_to_end_tests/golden-record/my_test_api_client/api/tests/callback_test.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from http import HTTPStatus
2-
from typing import Any, Dict, Optional, Union, cast
2+
from typing import Any, Dict, Optional, Union
33

44
import httpx
55

@@ -27,7 +27,7 @@ def _parse_response(
2727
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
2828
) -> Optional[Union[Any, HTTPValidationError]]:
2929
if response.status_code == HTTPStatus.OK:
30-
response_200 = cast(Any, response.json())
30+
response_200 = response.json()
3131
return response_200
3232
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
3333
response_422 = HTTPValidationError.from_dict(response.json())

end_to_end_tests/golden-record/my_test_api_client/api/tests/defaults_tests_defaults_post.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import datetime
22
from http import HTTPStatus
3-
from typing import Any, Dict, List, Optional, Union, cast
3+
from typing import Any, Dict, List, Optional, Union
44

55
import httpx
66
from dateutil.parser import isoparse
@@ -94,7 +94,7 @@ def _parse_response(
9494
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
9595
) -> Optional[Union[Any, HTTPValidationError]]:
9696
if response.status_code == HTTPStatus.OK:
97-
response_200 = cast(Any, response.json())
97+
response_200 = response.json()
9898
return response_200
9999
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
100100
response_422 = HTTPValidationError.from_dict(response.json())

end_to_end_tests/golden-record/my_test_api_client/api/tests/int_enum_tests_int_enum_post.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from http import HTTPStatus
2-
from typing import Any, Dict, Optional, Union, cast
2+
from typing import Any, Dict, Optional, Union
33

44
import httpx
55

@@ -32,7 +32,7 @@ def _parse_response(
3232
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
3333
) -> Optional[Union[Any, HTTPValidationError]]:
3434
if response.status_code == HTTPStatus.OK:
35-
response_200 = cast(Any, response.json())
35+
response_200 = response.json()
3636
return response_200
3737
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
3838
response_422 = HTTPValidationError.from_dict(response.json())

end_to_end_tests/golden-record/my_test_api_client/api/tests/json_body_tests_json_body_post.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from http import HTTPStatus
2-
from typing import Any, Dict, Optional, Union, cast
2+
from typing import Any, Dict, Optional, Union
33

44
import httpx
55

@@ -27,7 +27,7 @@ def _parse_response(
2727
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
2828
) -> Optional[Union[Any, HTTPValidationError]]:
2929
if response.status_code == HTTPStatus.OK:
30-
response_200 = cast(Any, response.json())
30+
response_200 = response.json()
3131
return response_200
3232
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
3333
response_422 = HTTPValidationError.from_dict(response.json())

end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_file_tests_upload_post.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from http import HTTPStatus
2-
from typing import Any, Dict, Optional, Union, cast
2+
from typing import Any, Dict, Optional, Union
33

44
import httpx
55

@@ -27,7 +27,7 @@ def _parse_response(
2727
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
2828
) -> Optional[Union[Any, HTTPValidationError]]:
2929
if response.status_code == HTTPStatus.OK:
30-
response_200 = cast(Any, response.json())
30+
response_200 = response.json()
3131
return response_200
3232
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
3333
response_422 = HTTPValidationError.from_dict(response.json())

end_to_end_tests/golden-record/my_test_api_client/api/tests/upload_multiple_files_tests_upload_post.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from http import HTTPStatus
2-
from typing import Any, Dict, List, Optional, Union, cast
2+
from typing import Any, Dict, List, Optional, Union
33

44
import httpx
55

@@ -30,7 +30,7 @@ def _parse_response(
3030
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
3131
) -> Optional[Union[Any, HTTPValidationError]]:
3232
if response.status_code == HTTPStatus.OK:
33-
response_200 = cast(Any, response.json())
33+
response_200 = response.json()
3434
return response_200
3535
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
3636
response_422 = HTTPValidationError.from_dict(response.json())

end_to_end_tests/openapi.json

+21
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,27 @@
781781
}
782782
}
783783
},
784+
"/responses/text": {
785+
"post": {
786+
"tags": [
787+
"responses"
788+
],
789+
"summary": "Text Response",
790+
"operationId": "text_response",
791+
"responses": {
792+
"200": {
793+
"description": "Text response",
794+
"content": {
795+
"text/plain": {
796+
"schema": {
797+
"type": "string"
798+
}
799+
}
800+
}
801+
}
802+
}
803+
}
804+
},
784805
"/auth/token_with_cookie": {
785806
"get": {
786807
"tags": [

0 commit comments

Comments
 (0)