Skip to content

Commit bab5ca2

Browse files
authored
[python] add async httpx support (#22021)
* [python] fix #19255 add async httpx support * update docs * 1. "async" parameter for templates 2. hand written tests for python-httpx 3. CI workflow updated * fix mypy
1 parent c1931c1 commit bab5ca2

File tree

409 files changed

+46512
-39
lines changed

Some content is hidden

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

409 files changed

+46512
-39
lines changed

.github/workflows/samples-python-petstore.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ jobs:
3434
- "3.13"
3535
sample:
3636
- samples/openapi3/client/petstore/python-aiohttp
37+
- samples/openapi3/client/petstore/python-httpx
3738
- samples/openapi3/client/petstore/python
3839
- samples/openapi3/client/petstore/python-lazyImports
3940
services:

bin/configs/python-httpx.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
generatorName: python
2+
outputDir: samples/openapi3/client/petstore/python-httpx
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/python
5+
library: httpx
6+
additionalProperties:
7+
packageName: petstore_api
8+
mapNumberTo: float
9+
poetry1: true
10+
nameMappings:
11+
_type: underscore_type
12+
type_: type_with_underscore
13+
modelNameMappings:
14+
# The OpenAPI spec ApiResponse conflicts with the internal ApiResponse
15+
ApiResponse: ModelApiResponse

docs/generators/python.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
2525
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
2626
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
2727
|lazyImports|Enable lazy imports.| |false|
28-
|library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3| |urllib3|
28+
|library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3, httpx| |urllib3|
2929
|mapNumberTo|Map number to Union[StrictFloat, StrictInt], StrictStr or float.| |Union[StrictFloat, StrictInt]|
3030
|packageName|python package name (convention: snake_case).| |openapi_client|
3131
|packageUrl|python package URL.| |null|

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ public PythonClientCodegen() {
157157
supportedLibraries.put("urllib3", "urllib3-based client");
158158
supportedLibraries.put("asyncio", "asyncio-based client");
159159
supportedLibraries.put("tornado", "tornado-based client (deprecated)");
160-
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use: asyncio, tornado (deprecated), urllib3");
160+
supportedLibraries.put("httpx", "httpx-based client");
161+
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use: asyncio, tornado (deprecated), urllib3, httpx");
161162
libraryOption.setDefault(DEFAULT_LIBRARY);
162163
cliOptions.add(libraryOption);
163164
setLibrary(DEFAULT_LIBRARY);
@@ -330,10 +331,15 @@ public void processOpts() {
330331

331332
if ("asyncio".equals(getLibrary())) {
332333
supportingFiles.add(new SupportingFile("asyncio/rest.mustache", packagePath(), "rest.py"));
334+
additionalProperties.put("async", "true");
333335
additionalProperties.put("asyncio", "true");
334336
} else if ("tornado".equals(getLibrary())) {
335337
supportingFiles.add(new SupportingFile("tornado/rest.mustache", packagePath(), "rest.py"));
336338
additionalProperties.put("tornado", "true");
339+
} else if ("httpx".equals(getLibrary())) {
340+
supportingFiles.add(new SupportingFile("httpx/rest.mustache", packagePath(), "rest.py"));
341+
additionalProperties.put("async", "true");
342+
additionalProperties.put("httpx", "true");
337343
} else {
338344
supportingFiles.add(new SupportingFile("rest.mustache", packagePath(), "rest.py"));
339345
}

modules/openapi-generator/src/main/resources/python/README_onlypackage.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ To be able to use it, you will need these dependencies in your own package that
2828

2929
* urllib3 >= 2.1.0, < 3.0.0
3030
* python-dateutil >= 2.8.2
31-
{{#asyncio}}
31+
{{#async}}
3232
* aiohttp >= 3.8.4
3333
* aiohttp-retry >= 2.8.3
34-
{{/asyncio}}
34+
{{/async}}
3535
{{#tornado}}
3636
* tornado >= 4.2, < 5
3737
{{/tornado}}

modules/openapi-generator/src/main/resources/python/api.mustache

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,40 +32,40 @@ class {{classname}}:
3232

3333

3434
@validate_call
35-
{{#asyncio}}async {{/asyncio}}def {{operationId}}{{>partial_api_args}} -> {{{returnType}}}{{^returnType}}None{{/returnType}}:
35+
{{#async}}async {{/async}}def {{operationId}}{{>partial_api_args}} -> {{{returnType}}}{{^returnType}}None{{/returnType}}:
3636
{{>partial_api}}
3737

38-
response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api(
38+
response_data = {{#async}}await {{/async}}self.api_client.call_api(
3939
*_param,
4040
_request_timeout=_request_timeout
4141
)
42-
{{#asyncio}}await {{/asyncio}}response_data.read()
42+
{{#async}}await {{/async}}response_data.read()
4343
return self.api_client.response_deserialize(
4444
response_data=response_data,
4545
response_types_map=_response_types_map,
4646
).data
4747

4848

4949
@validate_call
50-
{{#asyncio}}async {{/asyncio}}def {{operationId}}_with_http_info{{>partial_api_args}} -> ApiResponse[{{{returnType}}}{{^returnType}}None{{/returnType}}]:
50+
{{#async}}async {{/async}}def {{operationId}}_with_http_info{{>partial_api_args}} -> ApiResponse[{{{returnType}}}{{^returnType}}None{{/returnType}}]:
5151
{{>partial_api}}
5252

53-
response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api(
53+
response_data = {{#async}}await {{/async}}self.api_client.call_api(
5454
*_param,
5555
_request_timeout=_request_timeout
5656
)
57-
{{#asyncio}}await {{/asyncio}}response_data.read()
57+
{{#async}}await {{/async}}response_data.read()
5858
return self.api_client.response_deserialize(
5959
response_data=response_data,
6060
response_types_map=_response_types_map,
6161
)
6262

6363

6464
@validate_call
65-
{{#asyncio}}async {{/asyncio}}def {{operationId}}_without_preload_content{{>partial_api_args}} -> RESTResponseType:
65+
{{#async}}async {{/async}}def {{operationId}}_without_preload_content{{>partial_api_args}} -> RESTResponseType:
6666
{{>partial_api}}
6767

68-
response_data = {{#asyncio}}await {{/asyncio}}self.api_client.call_api(
68+
response_data = {{#async}}await {{/async}}self.api_client.call_api(
6969
*_param,
7070
_request_timeout=_request_timeout
7171
)

modules/openapi-generator/src/main/resources/python/api_client.mustache

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class ApiClient:
8888
self.user_agent = '{{{httpUserAgent}}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}'
8989
self.client_side_validation = configuration.client_side_validation
9090

91-
{{#asyncio}}
91+
{{#async}}
9292
async def __aenter__(self):
9393
return self
9494

@@ -97,14 +97,14 @@ class ApiClient:
9797

9898
async def close(self):
9999
await self.rest_client.close()
100-
{{/asyncio}}
101-
{{^asyncio}}
100+
{{/async}}
101+
{{^async}}
102102
def __enter__(self):
103103
return self
104104

105105
def __exit__(self, exc_type, exc_value, traceback):
106106
pass
107-
{{/asyncio}}
107+
{{/async}}
108108

109109
@property
110110
def user_agent(self):
@@ -257,7 +257,7 @@ class ApiClient:
257257
{{#tornado}}
258258
@tornado.gen.coroutine
259259
{{/tornado}}
260-
{{#asyncio}}async {{/asyncio}}def call_api(
260+
{{#async}}async {{/async}}def call_api(
261261
self,
262262
method,
263263
url,
@@ -280,7 +280,7 @@ class ApiClient:
280280

281281
try:
282282
# perform request and return response
283-
response_data = {{#asyncio}}await {{/asyncio}}{{#tornado}}yield {{/tornado}}self.rest_client.request(
283+
response_data = {{#async}}await {{/async}}{{#tornado}}yield {{/tornado}}self.rest_client.request(
284284
method, url,
285285
headers=header_params,
286286
body=body, post_params=post_params,

modules/openapi-generator/src/main/resources/python/api_doc_example.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ from pprint import pprint
1010
{{> python_doc_auth_partial}}
1111

1212
# Enter a context with an instance of the API client
13-
{{#asyncio}}async {{/asyncio}}with {{{packageName}}}.ApiClient(configuration) as api_client:
13+
{{#async}}async {{/async}}with {{{packageName}}}.ApiClient(configuration) as api_client:
1414
# Create an instance of the API class
1515
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
1616
{{#allParams}}
@@ -21,7 +21,7 @@ from pprint import pprint
2121
{{#summary}}
2222
# {{{.}}}
2323
{{/summary}}
24-
{{#returnType}}api_response = {{/returnType}}{{#asyncio}}await {{/asyncio}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
24+
{{#returnType}}api_response = {{/returnType}}{{#async}}await {{/async}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
2525
{{#returnType}}
2626
print("The response of {{classname}}->{{operationId}}:\n")
2727
pprint(api_response)

modules/openapi-generator/src/main/resources/python/api_test.mustache

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,31 @@ import unittest
88
from {{apiPackage}}.{{classFilename}} import {{classname}}
99

1010

11-
class {{#operations}}Test{{classname}}(unittest.{{#asyncio}}IsolatedAsyncio{{/asyncio}}TestCase):
11+
class {{#operations}}Test{{classname}}(unittest.{{#async}}IsolatedAsyncio{{/async}}TestCase):
1212
"""{{classname}} unit test stubs"""
1313

14-
{{#asyncio}}
14+
{{#async}}
1515
async def asyncSetUp(self) -> None:
1616
self.api = {{classname}}()
1717

1818
async def asyncTearDown(self) -> None:
1919
await self.api.api_client.close()
20-
{{/asyncio}}
21-
{{^asyncio}}
20+
{{/async}}
21+
{{^async}}
2222
def setUp(self) -> None:
2323
self.api = {{classname}}()
2424

2525
def tearDown(self) -> None:
2626
pass
27-
{{/asyncio}}
27+
{{/async}}
2828

2929
{{#operation}}
30-
{{#asyncio}}
30+
{{#async}}
3131
async def test_{{operationId}}(self) -> None:
32-
{{/asyncio}}
33-
{{^asyncio}}
32+
{{/async}}
33+
{{^async}}
3434
def test_{{operationId}}(self) -> None:
35-
{{/asyncio}}
35+
{{/async}}
3636
"""Test case for {{{operationId}}}
3737

3838
{{#summary}}

modules/openapi-generator/src/main/resources/python/common_README.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ from pprint import pprint
88

99

1010
# Enter a context with an instance of the API client
11-
{{#asyncio}}async {{/asyncio}}with {{{packageName}}}.ApiClient(configuration) as api_client:
11+
{{#async}}async {{/async}}with {{{packageName}}}.ApiClient(configuration) as api_client:
1212
# Create an instance of the API class
1313
api_instance = {{{packageName}}}.{{{classname}}}(api_client)
1414
{{#allParams}}
@@ -19,7 +19,7 @@ from pprint import pprint
1919
{{#summary}}
2020
# {{{.}}}
2121
{{/summary}}
22-
{{#returnType}}api_response = {{/returnType}}{{#asyncio}}await {{/asyncio}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
22+
{{#returnType}}api_response = {{/returnType}}{{#async}}await {{/async}}api_instance.{{{operationId}}}({{#allParams}}{{#required}}{{paramName}}{{/required}}{{^required}}{{paramName}}={{paramName}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}})
2323
{{#returnType}}
2424
print("The response of {{classname}}->{{operationId}}:\n")
2525
pprint(api_response)

0 commit comments

Comments
 (0)