From a28ce0b604b56206d414eb5c01b46fbac304aa05 Mon Sep 17 00:00:00 2001 From: Darshana Sanjeewan Adikari Date: Fri, 1 Sep 2017 12:18:03 +0200 Subject: [PATCH] [python-client] Thread pool fix (#6396) * [python-client] Added close() and join() for threadpool at ApiClient destructor. * Updated stuff for CI --- .../main/resources/python/api_client.mustache | 4 ++ .../python/.swagger-codegen/VERSION | 2 +- .../petstore-security-test/python/README.md | 2 +- .../python/petstore_api/api_client.py | 66 ++++++++++--------- .../python/petstore_api/apis/fake_api.py | 30 ++++----- .../petstore_api/models/model_return.py | 1 + .../python/petstore_api/rest.py | 7 +- .../petstore-security-test/python/setup.py | 2 +- .../client/petstore/python/docs/StoreApi.md | 2 +- .../python/petstore_api/api_client.py | 4 ++ 10 files changed, 61 insertions(+), 59 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/python/api_client.mustache b/modules/swagger-codegen/src/main/resources/python/api_client.mustache index 03e8653116a..967e1c5d293 100644 --- a/modules/swagger-codegen/src/main/resources/python/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/python/api_client.mustache @@ -66,6 +66,10 @@ class ApiClient(object): self.cookie = cookie # Set default User-Agent. self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}Swagger-Codegen/{{{packageVersion}}}/python{{/httpUserAgent}}' + + def __del__(self): + self.pool.close() + self.pool.join() @property def user_agent(self): diff --git a/samples/client/petstore-security-test/python/.swagger-codegen/VERSION b/samples/client/petstore-security-test/python/.swagger-codegen/VERSION index 7fea99011a6..f9f7450d135 100644 --- a/samples/client/petstore-security-test/python/.swagger-codegen/VERSION +++ b/samples/client/petstore-security-test/python/.swagger-codegen/VERSION @@ -1 +1 @@ -2.2.3-SNAPSHOT \ No newline at end of file +2.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore-security-test/python/README.md b/samples/client/petstore-security-test/python/README.md index 971b2c7c77a..9980cc6d7dd 100644 --- a/samples/client/petstore-security-test/python/README.md +++ b/samples/client/petstore-security-test/python/README.md @@ -1,4 +1,4 @@ -# petstore_api +# petstore-api This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ */ ' \" =end -- This Python package is automatically generated by the [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) project: diff --git a/samples/client/petstore-security-test/python/petstore_api/api_client.py b/samples/client/petstore-security-test/python/petstore_api/api_client.py index bfc88fd41d2..0325d3bad9e 100644 --- a/samples/client/petstore-security-test/python/petstore_api/api_client.py +++ b/samples/client/petstore-security-test/python/petstore_api/api_client.py @@ -16,7 +16,7 @@ import re import json import mimetypes import tempfile -import threading +from multiprocessing.pool import ThreadPool from datetime import date, datetime @@ -64,6 +64,7 @@ class ApiClient(object): configuration = Configuration() self.configuration = configuration + self.pool = ThreadPool() self.rest_client = RESTClientObject(configuration) self.default_headers = {} if header_name is not None: @@ -71,6 +72,10 @@ class ApiClient(object): self.cookie = cookie # Set default User-Agent. self.user_agent = 'Swagger-Codegen/1.0.0/python' + + def __del__(self): + self.pool.close() + self.pool.join() @property def user_agent(self): @@ -92,11 +97,11 @@ class ApiClient(object): def __call_api(self, resource_path, method, path_params=None, query_params=None, header_params=None, body=None, post_params=None, files=None, - response_type=None, auth_settings=None, callback=None, + response_type=None, auth_settings=None, _return_http_data_only=None, collection_formats=None, _preload_content=True, _request_timeout=None): - config = Configuration() + config = self.configuration # header parameters header_params = header_params or {} @@ -159,12 +164,7 @@ class ApiClient(object): else: return_data = None - if callback: - if _return_http_data_only: - callback(return_data) - else: - callback((return_data, response_data.status, response_data.getheaders())) - elif _return_http_data_only: + if _return_http_data_only: return (return_data) else: return (return_data, response_data.status, response_data.getheaders()) @@ -278,7 +278,7 @@ class ApiClient(object): def call_api(self, resource_path, method, path_params=None, query_params=None, header_params=None, body=None, post_params=None, files=None, - response_type=None, auth_settings=None, callback=None, + response_type=None, auth_settings=None, async=None, _return_http_data_only=None, collection_formats=None, _preload_content=True, _request_timeout=None): """ @@ -298,9 +298,7 @@ class ApiClient(object): :param response: Response data type. :param files dict: key -> filename, value -> filepath, for `multipart/form-data`. - :param callback function: Callback function for asynchronous request. - If provide this parameter, - the request will be called asynchronously. + :param async bool: execute request asynchronously :param _return_http_data_only: response data without head status code and headers :param collection_formats: dict of collection formats for path, query, header, and post parameters. @@ -315,22 +313,20 @@ class ApiClient(object): If parameter callback is None, then the method will return the response directly. """ - if callback is None: + if not async: return self.__call_api(resource_path, method, path_params, query_params, header_params, body, post_params, files, - response_type, auth_settings, callback, + response_type, auth_settings, _return_http_data_only, collection_formats, _preload_content, _request_timeout) else: - thread = threading.Thread(target=self.__call_api, - args=(resource_path, method, - path_params, query_params, - header_params, body, - post_params, files, - response_type, auth_settings, - callback, _return_http_data_only, - collection_formats, _preload_content, _request_timeout)) - thread.start() + thread = self.pool.apply_async(self.__call_api, (resource_path, method, + path_params, query_params, + header_params, body, + post_params, files, + response_type, auth_settings, + _return_http_data_only, + collection_formats, _preload_content, _request_timeout)) return thread def request(self, method, url, query_params=None, headers=None, @@ -610,17 +606,23 @@ class ApiClient(object): :param klass: class literal. :return: model object. """ - if not klass.swagger_types: + + if not klass.swagger_types and not hasattr(klass, 'get_real_child_model'): return data kwargs = {} - for attr, attr_type in iteritems(klass.swagger_types): - if data is not None \ - and klass.attribute_map[attr] in data \ - and isinstance(data, (list, dict)): - value = data[klass.attribute_map[attr]] - kwargs[attr] = self.__deserialize(value, attr_type) + if klass.swagger_types is not None: + for attr, attr_type in iteritems(klass.swagger_types): + if data is not None \ + and klass.attribute_map[attr] in data \ + and isinstance(data, (list, dict)): + value = data[klass.attribute_map[attr]] + kwargs[attr] = self.__deserialize(value, attr_type) - instance = klass(**kwargs) + instance = klass(**kwargs) + if hasattr(instance, 'get_real_child_model'): + klass_name = instance.get_real_child_model(data) + if klass_name: + instance = self.__deserialize(data, klass_name) return instance diff --git a/samples/client/petstore-security-test/python/petstore_api/apis/fake_api.py b/samples/client/petstore-security-test/python/petstore_api/apis/fake_api.py index ff031d77b73..34a566b1dbe 100644 --- a/samples/client/petstore-security-test/python/petstore_api/apis/fake_api.py +++ b/samples/client/petstore-security-test/python/petstore_api/apis/fake_api.py @@ -39,22 +39,18 @@ class FakeApi(object): """ To test code injection */ ' \" =end -- \\r\\n \\n \\r This method makes a synchronous HTTP request by default. To make an - asynchronous HTTP request, please define a `callback` function - to be invoked when receiving the response. - >>> def callback_function(response): - >>> pprint(response) - >>> - >>> thread = api.test_code_inject____end__rn_n_r(callback=callback_function) + asynchronous HTTP request, please pass async=True + >>> thread = api.test_code_inject____end__rn_n_r(async=True) + >>> result = thread.get() - :param callback function: The callback function - for asynchronous request. (optional) + :param async bool :param str test_code_inject____end____rn_n_r: To test code injection */ ' \" =end -- \\r\\n \\n \\r :return: None If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True - if kwargs.get('callback'): + if kwargs.get('async'): return self.test_code_inject____end__rn_n_r_with_http_info(**kwargs) else: (data) = self.test_code_inject____end__rn_n_r_with_http_info(**kwargs) @@ -64,15 +60,11 @@ class FakeApi(object): """ To test code injection */ ' \" =end -- \\r\\n \\n \\r This method makes a synchronous HTTP request by default. To make an - asynchronous HTTP request, please define a `callback` function - to be invoked when receiving the response. - >>> def callback_function(response): - >>> pprint(response) - >>> - >>> thread = api.test_code_inject____end__rn_n_r_with_http_info(callback=callback_function) + asynchronous HTTP request, please pass async=True + >>> thread = api.test_code_inject____end__rn_n_r_with_http_info(async=True) + >>> result = thread.get() - :param callback function: The callback function - for asynchronous request. (optional) + :param async bool :param str test_code_inject____end____rn_n_r: To test code injection */ ' \" =end -- \\r\\n \\n \\r :return: None If the method is called asynchronously, @@ -80,7 +72,7 @@ class FakeApi(object): """ all_params = ['test_code_inject____end____rn_n_r'] - all_params.append('callback') + all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') all_params.append('_request_timeout') @@ -130,7 +122,7 @@ class FakeApi(object): files=local_var_files, response_type=None, auth_settings=auth_settings, - callback=params.get('callback'), + async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), _preload_content=params.get('_preload_content', True), _request_timeout=params.get('_request_timeout'), diff --git a/samples/client/petstore-security-test/python/petstore_api/models/model_return.py b/samples/client/petstore-security-test/python/petstore_api/models/model_return.py index 6a57ba570a4..bb6813619ad 100644 --- a/samples/client/petstore-security-test/python/petstore_api/models/model_return.py +++ b/samples/client/petstore-security-test/python/petstore_api/models/model_return.py @@ -44,6 +44,7 @@ class ModelReturn(object): """ self.__return = None + self.discriminator = None if _return is not None: self._return = _return diff --git a/samples/client/petstore-security-test/python/petstore_api/rest.py b/samples/client/petstore-security-test/python/petstore_api/rest.py index 4f7c42a576c..46fbdbb63bb 100644 --- a/samples/client/petstore-security-test/python/petstore_api/rest.py +++ b/samples/client/petstore-security-test/python/petstore_api/rest.py @@ -61,8 +61,7 @@ class RESTClientObject(object): # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # maxsize is the number of requests to host that are allowed in parallel - # ca_certs vs cert_file vs key_file - # http://stackoverflow.com/a/23957365/2985775 + # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # cert_reqs if configuration.verify_ssl: @@ -142,7 +141,7 @@ class RESTClientObject(object): url += '?' + urlencode(query_params) if re.search('json', headers['Content-Type'], re.IGNORECASE): request_body = None - if body: + if body is not None: request_body = json.dumps(body) r = self.pool_manager.request(method, url, body=request_body, @@ -203,7 +202,7 @@ class RESTClientObject(object): # log response body logger.debug("response body: %s", r.data) - if r.status not in range(200, 206): + if not 200 <= r.status <= 299: raise ApiException(http_resp=r) return r diff --git a/samples/client/petstore-security-test/python/setup.py b/samples/client/petstore-security-test/python/setup.py index 3f8f4c118e1..d752ddcc05a 100644 --- a/samples/client/petstore-security-test/python/setup.py +++ b/samples/client/petstore-security-test/python/setup.py @@ -14,7 +14,7 @@ import sys from setuptools import setup, find_packages -NAME = "petstore_api" +NAME = "petstore-api" VERSION = "1.0.0" # To install the library, run the following # diff --git a/samples/client/petstore/python/docs/StoreApi.md b/samples/client/petstore/python/docs/StoreApi.md index fca66f6524d..a75113975ef 100644 --- a/samples/client/petstore/python/docs/StoreApi.md +++ b/samples/client/petstore/python/docs/StoreApi.md @@ -94,7 +94,7 @@ This endpoint does not need any parameter. ### Return type -[**dict(str, int)**](dict.md) +**dict(str, int)** ### Authorization diff --git a/samples/client/petstore/python/petstore_api/api_client.py b/samples/client/petstore/python/petstore_api/api_client.py index de8cd8abfe4..a5dea6bf6c5 100644 --- a/samples/client/petstore/python/petstore_api/api_client.py +++ b/samples/client/petstore/python/petstore_api/api_client.py @@ -72,6 +72,10 @@ class ApiClient(object): self.cookie = cookie # Set default User-Agent. self.user_agent = 'Swagger-Codegen/1.0.0/python' + + def __del__(self): + self.pool.close() + self.pool.join() @property def user_agent(self):