Skip to content

Commit 7f6b5b9

Browse files
committed
Merge branch 'main' into openapi-3.1
# Conflicts: # openapi_python_client/parser/openapi.py
2 parents a8eaef1 + 77ea9ff commit 7f6b5b9

27 files changed

+726
-153
lines changed

.changeset/remove_useless_pass_statements_from_generated_code.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/switch_from_black_to_ruff_for_formatting.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

.changeset/use_ruff_instead_of_isort_autoflake_at_runtime.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

.github/workflows/checks.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
steps:
1818
- uses: actions/checkout@v4.1.1
1919
- name: Set up Python
20-
uses: actions/setup-python@v4
20+
uses: actions/setup-python@v5.0.0
2121
with:
2222
python-version: ${{ matrix.python }}
2323

@@ -101,7 +101,7 @@ jobs:
101101
steps:
102102
- uses: actions/checkout@v4.1.1
103103
- name: Set up Python
104-
uses: actions/setup-python@v4
104+
uses: actions/setup-python@v5.0.0
105105
with:
106106
python-version: "3.8"
107107
- name: Get Python Version

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,40 @@ Programmatic usage of this project (e.g., importing it as a Python module) and t
1313

1414
The 0.x prefix used in versions for this project is to indicate that breaking changes are expected frequently (several times a year). Breaking changes will increment the minor number, all other changes will increment the patch number. You can track the progress toward 1.0 [here](https://github.com/openapi-generators/openapi-python-client/projects/2).
1515

16+
## 0.16.0 (2023-12-07)
17+
18+
### Breaking Changes
19+
20+
#### Switch from Black to Ruff for formatting
21+
22+
`black` is no longer a runtime dependency, so if you have them set in custom `post_hooks` in a config file, you'll need to make sure they're being installed manually. [`ruff`](https://docs.astral.sh/ruff) is now installed and used by default instead.
23+
24+
#### Use Ruff instead of isort + autoflake at runtime
25+
26+
`isort` and `autoflake` are no longer runtime dependencies, so if you have them set in custom `post_hooks` in a config file, you'll need to make sure they're being installed manually. [`ruff`](https://docs.astral.sh/ruff) is now installed and used by default instead.
27+
28+
### Features
29+
30+
#### Support all `text/*` content types in responses
31+
32+
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).
33+
34+
Thanks to @fdintino for the initial implementation, and thanks for the discussions from @kairntech, @rubenfiszel, and @antoneladestito.
35+
36+
Closes #797 and #821.
37+
38+
#### Support `application/octet-stream` request bodies
39+
40+
Endpoints that accept `application/octet-stream` request bodies are now supported using the same `File` type as octet-stream responses.
41+
42+
Thanks to @kgutwin for the implementation and @rtaycher for the discussion!
43+
44+
PR #899 closes #588
45+
46+
### Fixes
47+
48+
#### Remove useless `pass` statements from generated code
49+
1650
## 0.15.2 (2023-09-16)
1751

1852
### Features

end_to_end_tests/baseline_openapi_3.0.json

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,46 @@
625625
}
626626
}
627627
}
628+
},
629+
"post": {
630+
"tags": [
631+
"tests"
632+
],
633+
"summary": "Binary (octet stream) request body",
634+
"operationId": "octet_stream_tests_octet_stream_post",
635+
"requestBody": {
636+
"content": {
637+
"application/octet-stream": {
638+
"schema": {
639+
"description": "A file to upload",
640+
"type": "string",
641+
"format": "binary"
642+
}
643+
}
644+
}
645+
},
646+
"responses": {
647+
"200": {
648+
"description": "success",
649+
"content": {
650+
"application/json": {
651+
"schema": {
652+
"type": "string"
653+
}
654+
}
655+
}
656+
},
657+
"422": {
658+
"description": "Validation Error",
659+
"content": {
660+
"application/json": {
661+
"schema": {
662+
"$ref": "#/components/schemas/HTTPValidationError"
663+
}
664+
}
665+
}
666+
}
667+
}
628668
}
629669
},
630670
"/tests/no_response": {
@@ -781,6 +821,27 @@
781821
}
782822
}
783823
},
824+
"/responses/text": {
825+
"post": {
826+
"tags": [
827+
"responses"
828+
],
829+
"summary": "Text Response",
830+
"operationId": "text_response",
831+
"responses": {
832+
"200": {
833+
"description": "Text response",
834+
"content": {
835+
"text/plain": {
836+
"schema": {
837+
"type": "string"
838+
}
839+
}
840+
}
841+
}
842+
}
843+
}
844+
},
784845
"/auth/token_with_cookie": {
785846
"get": {
786847
"tags": [

end_to_end_tests/baseline_openapi_3.1.yaml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,46 @@ info:
623623
}
624624
}
625625
}
626+
},
627+
"post": {
628+
"tags": [
629+
"tests"
630+
],
631+
"summary": "Binary (octet stream) request body",
632+
"operationId": "octet_stream_tests_octet_stream_post",
633+
"requestBody": {
634+
"content": {
635+
"application/octet-stream": {
636+
"schema": {
637+
"description": "A file to upload",
638+
"type": "string",
639+
"format": "binary"
640+
}
641+
}
642+
}
643+
},
644+
"responses": {
645+
"200": {
646+
"description": "success",
647+
"content": {
648+
"application/json": {
649+
"schema": {
650+
"type": "string"
651+
}
652+
}
653+
}
654+
},
655+
"422": {
656+
"description": "Validation Error",
657+
"content": {
658+
"application/json": {
659+
"schema": {
660+
"$ref": "#/components/schemas/HTTPValidationError"
661+
}
662+
}
663+
}
664+
}
665+
}
626666
}
627667
},
628668
"/tests/no_response": {
@@ -787,6 +827,27 @@ info:
787827
}
788828
}
789829
},
830+
"/responses/text": {
831+
"post": {
832+
"tags": [
833+
"responses"
834+
],
835+
"summary": "Text Response",
836+
"operationId": "text_response",
837+
"responses": {
838+
"200": {
839+
"description": "Text response",
840+
"content": {
841+
"text/plain": {
842+
"schema": {
843+
"type": "string"
844+
}
845+
}
846+
}
847+
}
848+
}
849+
}
850+
},
790851
"/auth/token_with_cookie": {
791852
"get": {
792853
"tags": [

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

Lines changed: 8 additions & 1 deletion
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

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
json_body_tests_json_body_post,
1616
no_response_tests_no_response_get,
1717
octet_stream_tests_octet_stream_get,
18+
octet_stream_tests_octet_stream_post,
1819
post_form_data,
1920
post_form_data_inline,
2021
post_tests_json_body_string,
@@ -118,6 +119,13 @@ def octet_stream_tests_octet_stream_get(cls) -> types.ModuleType:
118119
"""
119120
return octet_stream_tests_octet_stream_get
120121

122+
@classmethod
123+
def octet_stream_tests_octet_stream_post(cls) -> types.ModuleType:
124+
"""
125+
Binary (octet stream) request body
126+
"""
127+
return octet_stream_tests_octet_stream_post
128+
121129
@classmethod
122130
def no_response_tests_no_response_get(cls) -> types.ModuleType:
123131
"""
Lines changed: 118 additions & 0 deletions
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

0 commit comments

Comments
 (0)