Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[python] Avoid creating unused ThreadPools #1387

Merged
merged 3 commits into from Nov 8, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/generators/rust-server.md
Expand Up @@ -5,6 +5,6 @@ CONFIG OPTIONS for rust-server
Rust crate name (convention: snake_case). (Default: openapi_client)

packageVersion
Rust crate version. (Default: 1.0.0)
Rust crate version.

Back to the [generators list](README.md)
Expand Up @@ -40,6 +40,8 @@ class ApiClient(object):
the API.
:param cookie: a cookie to include in the header when making calls
to the API
:param pool_threads: The number of threads to use for async requests
to the API. More threads means more concurrent API requests.
"""

PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types
Expand All @@ -53,14 +55,15 @@ class ApiClient(object):
'datetime': datetime.datetime,
'object': object,
}
_pool = None

def __init__(self, configuration=None, header_name=None, header_value=None,
cookie=None):
cookie=None, pool_threads=1):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like a breaking change. I suggest leaving =None and still using default number of threads. It shouldn't be a problem because the pool will be created on the first request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I'll set pool_thread=None

cc @minrk

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, this is not a breaking change, though it is a change in the default performance of concurrent async requests from the same client object. Tying concurrent IO to CPU count doesn't make a lot of sense to me, especially in Python. I'd argue that a fixed default pool size (if not 1, maybe 4 or 10) would be more logical than spawning 64 threads on a big machine, and would result in more consistent behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@minrk I agree with you. This default may not work well on a huge server but it's default, it may be optimal in general, this calculation may be changed in the future releases of Python. Someone can base on this behavior and it'd be a breaking change for him/her.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using pool_thread=None for current master to make it backward-compatible and using pool_threads=1 in 4.0.x (a major release with breaking changes scheduled to be released next month)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wing328 Sounds good to me.

if configuration is None:
configuration = Configuration()
self.configuration = configuration
self.pool_threads = pool_threads

self.pool = ThreadPool()
self.rest_client = rest.RESTClientObject(configuration)
self.default_headers = {}
if header_name is not None:
Expand All @@ -70,8 +73,19 @@ class ApiClient(object):
self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}'

def __del__(self):
self.pool.close()
self.pool.join()
if self._pool:
self._pool.close()
self._pool.join()
self._pool = None

@property
def pool(self):
"""Create thread pool on first request
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

avoids instantiating unused threadpool for blocking clients.
"""
if self._pool is None:
self._pool = ThreadPool(self.pool_threads)
return self._pool

@property
def user_agent(self):
Expand Down
@@ -1 +1 @@
3.3.2-SNAPSHOT
3.3.3-SNAPSHOT
1 change: 1 addition & 0 deletions samples/client/petstore/python-asyncio/README.md
Expand Up @@ -80,6 +80,7 @@ Class | Method | HTTP request | Description
*FakeApi* | [**test_client_model**](docs/FakeApi.md#test_client_model) | **PATCH** /fake | To test \"client\" model
*FakeApi* | [**test_endpoint_parameters**](docs/FakeApi.md#test_endpoint_parameters) | **POST** /fake | Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
*FakeApi* | [**test_enum_parameters**](docs/FakeApi.md#test_enum_parameters) | **GET** /fake | To test enum parameters
*FakeApi* | [**test_group_parameters**](docs/FakeApi.md#test_group_parameters) | **DELETE** /fake | Fake endpoint to test group parameters (optional)
*FakeApi* | [**test_inline_additional_properties**](docs/FakeApi.md#test_inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties
*FakeApi* | [**test_json_form_data**](docs/FakeApi.md#test_json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data
*FakeClassnameTags123Api* | [**test_classname**](docs/FakeClassnameTags123Api.md#test_classname) | **PATCH** /fake_classname_test | To test class name in snake case
Expand Down
52 changes: 52 additions & 0 deletions samples/client/petstore/python-asyncio/docs/FakeApi.md
Expand Up @@ -13,6 +13,7 @@ Method | HTTP request | Description
[**test_client_model**](FakeApi.md#test_client_model) | **PATCH** /fake | To test \"client\" model
[**test_endpoint_parameters**](FakeApi.md#test_endpoint_parameters) | **POST** /fake | Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
[**test_enum_parameters**](FakeApi.md#test_enum_parameters) | **GET** /fake | To test enum parameters
[**test_group_parameters**](FakeApi.md#test_group_parameters) | **DELETE** /fake | Fake endpoint to test group parameters (optional)
[**test_inline_additional_properties**](FakeApi.md#test_inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties
[**test_json_form_data**](FakeApi.md#test_json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data

Expand Down Expand Up @@ -486,6 +487,57 @@ No authorization required

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **test_group_parameters**
> test_group_parameters(string_group=string_group, boolean_group=boolean_group, int64_group=int64_group)

Fake endpoint to test group parameters (optional)

Fake endpoint to test group parameters (optional)

### Example
```python
from __future__ import print_function
import time
import petstore_api
from petstore_api.rest import ApiException
from pprint import pprint

# create an instance of the API class
api_instance = petstore_api.FakeApi()
string_group = 56 # int | String in group parameters (optional)
boolean_group = True # bool | Boolean in group parameters (optional)
int64_group = 56 # int | Integer in group parameters (optional)

try:
# Fake endpoint to test group parameters (optional)
api_instance.test_group_parameters(string_group=string_group, boolean_group=boolean_group, int64_group=int64_group)
except ApiException as e:
print("Exception when calling FakeApi->test_group_parameters: %s\n" % e)
```

### Parameters

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**string_group** | **int**| String in group parameters | [optional]
**boolean_group** | **bool**| Boolean in group parameters | [optional]
**int64_group** | **int**| Integer in group parameters | [optional]

### Return type

void (empty response body)

### Authorization

No authorization required

### HTTP request headers

- **Content-Type**: Not defined
- **Accept**: Not defined

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **test_inline_additional_properties**
> test_inline_additional_properties(request_body)

Expand Down
Expand Up @@ -1009,6 +1009,102 @@ def test_enum_parameters_with_http_info(self, **kwargs): # noqa: E501
_request_timeout=local_var_params.get('_request_timeout'),
collection_formats=collection_formats)

def test_group_parameters(self, **kwargs): # noqa: E501
"""Fake endpoint to test group parameters (optional) # noqa: E501

Fake endpoint to test group parameters (optional) # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.test_group_parameters(async_req=True)
>>> result = thread.get()

:param async_req bool
:param int string_group: String in group parameters
:param bool boolean_group: Boolean in group parameters
:param int int64_group: Integer in group parameters
:return: None
If the method is called asynchronously,
returns the request thread.
"""
kwargs['_return_http_data_only'] = True
if kwargs.get('async_req'):
return self.test_group_parameters_with_http_info(**kwargs) # noqa: E501
else:
(data) = self.test_group_parameters_with_http_info(**kwargs) # noqa: E501
return data

def test_group_parameters_with_http_info(self, **kwargs): # noqa: E501
"""Fake endpoint to test group parameters (optional) # noqa: E501

Fake endpoint to test group parameters (optional) # noqa: E501
This method makes a synchronous HTTP request by default. To make an
asynchronous HTTP request, please pass async_req=True
>>> thread = api.test_group_parameters_with_http_info(async_req=True)
>>> result = thread.get()

:param async_req bool
:param int string_group: String in group parameters
:param bool boolean_group: Boolean in group parameters
:param int int64_group: Integer in group parameters
:return: None
If the method is called asynchronously,
returns the request thread.
"""

local_var_params = locals()

all_params = ['string_group', 'boolean_group', 'int64_group'] # noqa: E501
all_params.append('async_req')
all_params.append('_return_http_data_only')
all_params.append('_preload_content')
all_params.append('_request_timeout')

for key, val in six.iteritems(local_var_params['kwargs']):
if key not in all_params:
raise TypeError(
"Got an unexpected keyword argument '%s'"
" to method test_group_parameters" % key
)
local_var_params[key] = val
del local_var_params['kwargs']

collection_formats = {}

path_params = {}

query_params = []
if 'string_group' in local_var_params:
query_params.append(('string_group', local_var_params['string_group'])) # noqa: E501
if 'int64_group' in local_var_params:
query_params.append(('int64_group', local_var_params['int64_group'])) # noqa: E501

header_params = {}
if 'boolean_group' in local_var_params:
header_params['boolean_group'] = local_var_params['boolean_group'] # noqa: E501

form_params = []
local_var_files = {}

body_params = None
# Authentication setting
auth_settings = [] # noqa: E501

return self.api_client.call_api(
'/fake', 'DELETE',
path_params,
query_params,
header_params,
body=body_params,
post_params=form_params,
files=local_var_files,
response_type=None, # noqa: E501
auth_settings=auth_settings,
async_req=local_var_params.get('async_req'),
_return_http_data_only=local_var_params.get('_return_http_data_only'), # noqa: E501
_preload_content=local_var_params.get('_preload_content', True),
_request_timeout=local_var_params.get('_request_timeout'),
collection_formats=collection_formats)

def test_inline_additional_properties(self, request_body, **kwargs): # noqa: E501
"""test inline additionalProperties # noqa: E501

Expand Down
22 changes: 18 additions & 4 deletions samples/client/petstore/python-asyncio/petstore_api/api_client.py
Expand Up @@ -45,6 +45,8 @@ class ApiClient(object):
the API.
:param cookie: a cookie to include in the header when making calls
to the API
:param pool_threads: The number of threads to use for async requests
to the API. More threads means more concurrent API requests.
"""

PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types
Expand All @@ -58,14 +60,15 @@ class ApiClient(object):
'datetime': datetime.datetime,
'object': object,
}
_pool = None

def __init__(self, configuration=None, header_name=None, header_value=None,
cookie=None):
cookie=None, pool_threads=1):
if configuration is None:
configuration = Configuration()
self.configuration = configuration
self.pool_threads = pool_threads

self.pool = ThreadPool()
self.rest_client = rest.RESTClientObject(configuration)
self.default_headers = {}
if header_name is not None:
Expand All @@ -75,8 +78,19 @@ def __init__(self, configuration=None, header_name=None, header_value=None,
self.user_agent = 'OpenAPI-Generator/1.0.0/python'

def __del__(self):
self.pool.close()
self.pool.join()
if self._pool:
self._pool.close()
self._pool.join()
self._pool = None

@property
def pool(self):
"""Create thread pool on first request
avoids instantiating unused threadpool for blocking clients.
"""
if self._pool is None:
self._pool = ThreadPool(self.pool_threads)
return self._pool

@property
def user_agent(self):
Expand Down
@@ -1 +1 @@
3.3.2-SNAPSHOT
3.3.3-SNAPSHOT
1 change: 1 addition & 0 deletions samples/client/petstore/python-tornado/README.md
Expand Up @@ -80,6 +80,7 @@ Class | Method | HTTP request | Description
*FakeApi* | [**test_client_model**](docs/FakeApi.md#test_client_model) | **PATCH** /fake | To test \"client\" model
*FakeApi* | [**test_endpoint_parameters**](docs/FakeApi.md#test_endpoint_parameters) | **POST** /fake | Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
*FakeApi* | [**test_enum_parameters**](docs/FakeApi.md#test_enum_parameters) | **GET** /fake | To test enum parameters
*FakeApi* | [**test_group_parameters**](docs/FakeApi.md#test_group_parameters) | **DELETE** /fake | Fake endpoint to test group parameters (optional)
*FakeApi* | [**test_inline_additional_properties**](docs/FakeApi.md#test_inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties
*FakeApi* | [**test_json_form_data**](docs/FakeApi.md#test_json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data
*FakeClassnameTags123Api* | [**test_classname**](docs/FakeClassnameTags123Api.md#test_classname) | **PATCH** /fake_classname_test | To test class name in snake case
Expand Down
52 changes: 52 additions & 0 deletions samples/client/petstore/python-tornado/docs/FakeApi.md
Expand Up @@ -13,6 +13,7 @@ Method | HTTP request | Description
[**test_client_model**](FakeApi.md#test_client_model) | **PATCH** /fake | To test \"client\" model
[**test_endpoint_parameters**](FakeApi.md#test_endpoint_parameters) | **POST** /fake | Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
[**test_enum_parameters**](FakeApi.md#test_enum_parameters) | **GET** /fake | To test enum parameters
[**test_group_parameters**](FakeApi.md#test_group_parameters) | **DELETE** /fake | Fake endpoint to test group parameters (optional)
[**test_inline_additional_properties**](FakeApi.md#test_inline_additional_properties) | **POST** /fake/inline-additionalProperties | test inline additionalProperties
[**test_json_form_data**](FakeApi.md#test_json_form_data) | **GET** /fake/jsonFormData | test json serialization of form data

Expand Down Expand Up @@ -486,6 +487,57 @@ No authorization required

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **test_group_parameters**
> test_group_parameters(string_group=string_group, boolean_group=boolean_group, int64_group=int64_group)

Fake endpoint to test group parameters (optional)

Fake endpoint to test group parameters (optional)

### Example
```python
from __future__ import print_function
import time
import petstore_api
from petstore_api.rest import ApiException
from pprint import pprint

# create an instance of the API class
api_instance = petstore_api.FakeApi()
string_group = 56 # int | String in group parameters (optional)
boolean_group = True # bool | Boolean in group parameters (optional)
int64_group = 56 # int | Integer in group parameters (optional)

try:
# Fake endpoint to test group parameters (optional)
api_instance.test_group_parameters(string_group=string_group, boolean_group=boolean_group, int64_group=int64_group)
except ApiException as e:
print("Exception when calling FakeApi->test_group_parameters: %s\n" % e)
```

### Parameters

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**string_group** | **int**| String in group parameters | [optional]
**boolean_group** | **bool**| Boolean in group parameters | [optional]
**int64_group** | **int**| Integer in group parameters | [optional]

### Return type

void (empty response body)

### Authorization

No authorization required

### HTTP request headers

- **Content-Type**: Not defined
- **Accept**: Not defined

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **test_inline_additional_properties**
> test_inline_additional_properties(request_body)

Expand Down