github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/python/python-experimental/api_client.mustache (about) 1 # coding: utf-8 2 {{>partial_header}} 3 from __future__ import absolute_import 4 5 import json 6 import atexit 7 import mimetypes 8 from multiprocessing.pool import ThreadPool 9 import os 10 11 # python 2 and python 3 compatibility library 12 import six 13 from six.moves.urllib.parse import quote 14 {{#tornado}} 15 import tornado.gen 16 {{/tornado}} 17 18 from {{packageName}} import rest 19 from {{packageName}}.configuration import Configuration 20 from {{packageName}}.exceptions import ApiValueError 21 from {{packageName}}.model_utils import ( 22 ModelNormal, 23 ModelSimple, 24 ModelComposed, 25 date, 26 datetime, 27 deserialize_file, 28 file_type, 29 model_to_dict, 30 str, 31 validate_and_convert_types 32 ) 33 34 35 class ApiClient(object): 36 """Generic API client for OpenAPI client library builds. 37 38 OpenAPI generic API client. This client handles the client- 39 server communication, and is invariant across implementations. Specifics of 40 the methods and models for each application are generated from the OpenAPI 41 templates. 42 43 NOTE: This class is auto generated by OpenAPI Generator. 44 Ref: https://openapi-generator.tech 45 Do not edit the class manually. 46 47 :param configuration: .Configuration object for this client 48 :param header_name: a header to pass when making calls to the API. 49 :param header_value: a header value to pass when making calls to 50 the API. 51 :param cookie: a cookie to include in the header when making calls 52 to the API 53 :param pool_threads: The number of threads to use for async requests 54 to the API. More threads means more concurrent API requests. 55 """ 56 57 # six.binary_type python2=str, python3=bytes 58 # six.text_type python2=unicode, python3=str 59 PRIMITIVE_TYPES = ( 60 (float, bool, six.binary_type, six.text_type) + six.integer_types 61 ) 62 _pool = None 63 64 def __init__(self, configuration=None, header_name=None, header_value=None, 65 cookie=None, pool_threads=1): 66 if configuration is None: 67 configuration = Configuration() 68 self.configuration = configuration 69 self.pool_threads = pool_threads 70 71 self.rest_client = rest.RESTClientObject(configuration) 72 self.default_headers = {} 73 if header_name is not None: 74 self.default_headers[header_name] = header_value 75 self.cookie = cookie 76 # Set default User-Agent. 77 self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}' 78 79 def __enter__(self): 80 return self 81 82 def __exit__(self, exc_type, exc_value, traceback): 83 self.close() 84 85 def close(self): 86 if self._pool: 87 self._pool.close() 88 self._pool.join() 89 self._pool = None 90 if hasattr(atexit, 'unregister'): 91 atexit.unregister(self.close) 92 93 @property 94 def pool(self): 95 """Create thread pool on first request 96 avoids instantiating unused threadpool for blocking clients. 97 """ 98 if self._pool is None: 99 atexit.register(self.close) 100 self._pool = ThreadPool(self.pool_threads) 101 return self._pool 102 103 @property 104 def user_agent(self): 105 """User agent for this API client""" 106 return self.default_headers['User-Agent'] 107 108 @user_agent.setter 109 def user_agent(self, value): 110 self.default_headers['User-Agent'] = value 111 112 def set_default_header(self, header_name, header_value): 113 self.default_headers[header_name] = header_value 114 115 {{#tornado}} 116 @tornado.gen.coroutine 117 {{/tornado}} 118 {{#asyncio}}async {{/asyncio}}def __call_api( 119 self, resource_path, method, path_params=None, 120 query_params=None, header_params=None, body=None, post_params=None, 121 files=None, response_type=None, auth_settings=None, 122 _return_http_data_only=None, collection_formats=None, 123 _preload_content=True, _request_timeout=None, _host=None, 124 _check_type=None): 125 126 config = self.configuration 127 128 # header parameters 129 header_params = header_params or {} 130 header_params.update(self.default_headers) 131 if self.cookie: 132 header_params['Cookie'] = self.cookie 133 if header_params: 134 header_params = self.sanitize_for_serialization(header_params) 135 header_params = dict(self.parameters_to_tuples(header_params, 136 collection_formats)) 137 138 # path parameters 139 if path_params: 140 path_params = self.sanitize_for_serialization(path_params) 141 path_params = self.parameters_to_tuples(path_params, 142 collection_formats) 143 for k, v in path_params: 144 # specified safe chars, encode everything 145 resource_path = resource_path.replace( 146 '{%s}' % k, 147 quote(str(v), safe=config.safe_chars_for_path_param) 148 ) 149 150 # query parameters 151 if query_params: 152 query_params = self.sanitize_for_serialization(query_params) 153 query_params = self.parameters_to_tuples(query_params, 154 collection_formats) 155 156 # post parameters 157 if post_params or files: 158 post_params = post_params if post_params else [] 159 post_params = self.sanitize_for_serialization(post_params) 160 post_params = self.parameters_to_tuples(post_params, 161 collection_formats) 162 post_params.extend(self.files_parameters(files)) 163 164 # body 165 if body: 166 body = self.sanitize_for_serialization(body) 167 168 # auth setting 169 self.update_params_for_auth(header_params, query_params, 170 auth_settings, resource_path, method, body) 171 172 # request url 173 if _host is None: 174 url = self.configuration.host + resource_path 175 else: 176 # use server/host defined in path or operation instead 177 url = _host + resource_path 178 179 # perform request and return response 180 response_data = {{#asyncio}}await {{/asyncio}}{{#tornado}}yield {{/tornado}}self.request( 181 method, url, query_params=query_params, headers=header_params, 182 post_params=post_params, body=body, 183 _preload_content=_preload_content, 184 _request_timeout=_request_timeout) 185 186 self.last_response = response_data 187 188 return_data = response_data 189 if _preload_content: 190 # deserialize response data 191 if response_type: 192 return_data = self.deserialize( 193 response_data, 194 response_type, 195 _check_type 196 ) 197 else: 198 return_data = None 199 200 {{^tornado}} 201 if _return_http_data_only: 202 return (return_data) 203 else: 204 return (return_data, response_data.status, 205 response_data.getheaders()) 206 {{/tornado}} 207 {{#tornado}} 208 if _return_http_data_only: 209 raise tornado.gen.Return(return_data) 210 else: 211 raise tornado.gen.Return((return_data, response_data.status, 212 response_data.getheaders())) 213 {{/tornado}} 214 215 def sanitize_for_serialization(self, obj): 216 """Builds a JSON POST object. 217 218 If obj is None, return None. 219 If obj is str, int, long, float, bool, return directly. 220 If obj is datetime.datetime, datetime.date 221 convert to string in iso8601 format. 222 If obj is list, sanitize each element in the list. 223 If obj is dict, return the dict. 224 If obj is OpenAPI model, return the properties dict. 225 226 :param obj: The data to serialize. 227 :return: The serialized form of data. 228 """ 229 if obj is None: 230 return None 231 elif isinstance(obj, self.PRIMITIVE_TYPES): 232 return obj 233 elif isinstance(obj, list): 234 return [self.sanitize_for_serialization(sub_obj) 235 for sub_obj in obj] 236 elif isinstance(obj, tuple): 237 return tuple(self.sanitize_for_serialization(sub_obj) 238 for sub_obj in obj) 239 elif isinstance(obj, (datetime, date)): 240 return obj.isoformat() 241 242 if isinstance(obj, dict): 243 obj_dict = obj 244 elif isinstance(obj, ModelNormal) or isinstance(obj, ModelComposed): 245 # Convert model obj to dict 246 # Convert attribute name to json key in 247 # model definition for request 248 obj_dict = model_to_dict(obj, serialize=True) 249 elif isinstance(obj, ModelSimple): 250 return self.sanitize_for_serialization(obj.value) 251 252 return {key: self.sanitize_for_serialization(val) 253 for key, val in six.iteritems(obj_dict)} 254 255 def deserialize(self, response, response_type, _check_type): 256 """Deserializes response into an object. 257 258 :param response: RESTResponse object to be deserialized. 259 :param response_type: For the response, a tuple containing: 260 valid classes 261 a list containing valid classes (for list schemas) 262 a dict containing a tuple of valid classes as the value 263 Example values: 264 (str,) 265 (Pet,) 266 (float, none_type) 267 ([int, none_type],) 268 ({str: (bool, str, int, float, date, datetime, str, none_type)},) 269 :param _check_type: boolean, whether to check the types of the data 270 received from the server 271 272 :return: deserialized object. 273 """ 274 # handle file downloading 275 # save response body into a tmp file and return the instance 276 if response_type == (file_type,): 277 content_disposition = response.getheader("Content-Disposition") 278 return deserialize_file(response.data, self.configuration, 279 content_disposition=content_disposition) 280 281 # fetch data from response object 282 try: 283 received_data = json.loads(response.data) 284 except ValueError: 285 received_data = response.data 286 287 # store our data under the key of 'received_data' so users have some 288 # context if they are deserializing a string and the data type is wrong 289 deserialized_data = validate_and_convert_types( 290 received_data, 291 response_type, 292 ['received_data'], 293 True, 294 _check_type, 295 configuration=self.configuration 296 ) 297 return deserialized_data 298 299 def call_api(self, resource_path, method, 300 path_params=None, query_params=None, header_params=None, 301 body=None, post_params=None, files=None, 302 response_type=None, auth_settings=None, async_req=None, 303 _return_http_data_only=None, collection_formats=None, 304 _preload_content=True, _request_timeout=None, _host=None, 305 _check_type=None): 306 """Makes the HTTP request (synchronous) and returns deserialized data. 307 308 To make an async_req request, set the async_req parameter. 309 310 :param resource_path: Path to method endpoint. 311 :param method: Method to call. 312 :param path_params: Path parameters in the url. 313 :param query_params: Query parameters in the url. 314 :param header_params: Header parameters to be 315 placed in the request header. 316 :param body: Request body. 317 :param post_params dict: Request post form parameters, 318 for `application/x-www-form-urlencoded`, `multipart/form-data`. 319 :param auth_settings list: Auth Settings names for the request. 320 :param response_type: For the response, a tuple containing: 321 valid classes 322 a list containing valid classes (for list schemas) 323 a dict containing a tuple of valid classes as the value 324 Example values: 325 (str,) 326 (Pet,) 327 (float, none_type) 328 ([int, none_type],) 329 ({str: (bool, str, int, float, date, datetime, str, none_type)},) 330 :param files dict: key -> field name, value -> a list of open file 331 objects for `multipart/form-data`. 332 :param async_req bool: execute request asynchronously 333 :param _return_http_data_only: response data without head status code 334 and headers 335 :param collection_formats: dict of collection formats for path, query, 336 header, and post parameters. 337 :param _preload_content: if False, the urllib3.HTTPResponse object will 338 be returned without reading/decoding response 339 data. Default is True. 340 :param _request_timeout: timeout setting for this request. If one 341 number provided, it will be total request 342 timeout. It can also be a pair (tuple) of 343 (connection, read) timeouts. 344 :param _check_type: boolean describing if the data back from the server 345 should have its type checked. 346 :return: 347 If async_req parameter is True, 348 the request will be called asynchronously. 349 The method will return the request thread. 350 If parameter async_req is False or missing, 351 then the method will return the response directly. 352 """ 353 if not async_req: 354 return self.__call_api(resource_path, method, 355 path_params, query_params, header_params, 356 body, post_params, files, 357 response_type, auth_settings, 358 _return_http_data_only, collection_formats, 359 _preload_content, _request_timeout, _host, 360 _check_type) 361 362 return self.pool.apply_async(self.__call_api, (resource_path, 363 method, path_params, 364 query_params, 365 header_params, body, 366 post_params, files, 367 response_type, 368 auth_settings, 369 _return_http_data_only, 370 collection_formats, 371 _preload_content, 372 _request_timeout, 373 _host, _check_type)) 374 375 def request(self, method, url, query_params=None, headers=None, 376 post_params=None, body=None, _preload_content=True, 377 _request_timeout=None): 378 """Makes the HTTP request using RESTClient.""" 379 if method == "GET": 380 return self.rest_client.GET(url, 381 query_params=query_params, 382 _preload_content=_preload_content, 383 _request_timeout=_request_timeout, 384 headers=headers) 385 elif method == "HEAD": 386 return self.rest_client.HEAD(url, 387 query_params=query_params, 388 _preload_content=_preload_content, 389 _request_timeout=_request_timeout, 390 headers=headers) 391 elif method == "OPTIONS": 392 return self.rest_client.OPTIONS(url, 393 query_params=query_params, 394 headers=headers, 395 post_params=post_params, 396 _preload_content=_preload_content, 397 _request_timeout=_request_timeout, 398 body=body) 399 elif method == "POST": 400 return self.rest_client.POST(url, 401 query_params=query_params, 402 headers=headers, 403 post_params=post_params, 404 _preload_content=_preload_content, 405 _request_timeout=_request_timeout, 406 body=body) 407 elif method == "PUT": 408 return self.rest_client.PUT(url, 409 query_params=query_params, 410 headers=headers, 411 post_params=post_params, 412 _preload_content=_preload_content, 413 _request_timeout=_request_timeout, 414 body=body) 415 elif method == "PATCH": 416 return self.rest_client.PATCH(url, 417 query_params=query_params, 418 headers=headers, 419 post_params=post_params, 420 _preload_content=_preload_content, 421 _request_timeout=_request_timeout, 422 body=body) 423 elif method == "DELETE": 424 return self.rest_client.DELETE(url, 425 query_params=query_params, 426 headers=headers, 427 _preload_content=_preload_content, 428 _request_timeout=_request_timeout, 429 body=body) 430 else: 431 raise ApiValueError( 432 "http method must be `GET`, `HEAD`, `OPTIONS`," 433 " `POST`, `PATCH`, `PUT` or `DELETE`." 434 ) 435 436 def parameters_to_tuples(self, params, collection_formats): 437 """Get parameters as list of tuples, formatting collections. 438 439 :param params: Parameters as dict or list of two-tuples 440 :param dict collection_formats: Parameter collection formats 441 :return: Parameters as list of tuples, collections formatted 442 """ 443 new_params = [] 444 if collection_formats is None: 445 collection_formats = {} 446 for k, v in six.iteritems(params) if isinstance(params, dict) else params: # noqa: E501 447 if k in collection_formats: 448 collection_format = collection_formats[k] 449 if collection_format == 'multi': 450 new_params.extend((k, value) for value in v) 451 else: 452 if collection_format == 'ssv': 453 delimiter = ' ' 454 elif collection_format == 'tsv': 455 delimiter = '\t' 456 elif collection_format == 'pipes': 457 delimiter = '|' 458 else: # csv is the default 459 delimiter = ',' 460 new_params.append( 461 (k, delimiter.join(str(value) for value in v))) 462 else: 463 new_params.append((k, v)) 464 return new_params 465 466 def files_parameters(self, files=None): 467 """Builds form parameters. 468 469 :param files: None or a dict with key=param_name and 470 value is a list of open file objects 471 :return: List of tuples of form parameters with file data 472 """ 473 if files is None: 474 return [] 475 476 params = [] 477 for param_name, file_instances in six.iteritems(files): 478 if file_instances is None: 479 # if the file field is nullable, skip None values 480 continue 481 for file_instance in file_instances: 482 if file_instance is None: 483 # if the file field is nullable, skip None values 484 continue 485 if file_instance.closed is True: 486 raise ApiValueError( 487 "Cannot read a closed file. The passed in file_type " 488 "for %s must be open." % param_name 489 ) 490 filename = os.path.basename(file_instance.name) 491 filedata = file_instance.read() 492 mimetype = (mimetypes.guess_type(filename)[0] or 493 'application/octet-stream') 494 params.append( 495 tuple([param_name, tuple([filename, filedata, mimetype])])) 496 file_instance.close() 497 498 return params 499 500 def select_header_accept(self, accepts): 501 """Returns `Accept` based on an array of accepts provided. 502 503 :param accepts: List of headers. 504 :return: Accept (e.g. application/json). 505 """ 506 if not accepts: 507 return 508 509 accepts = [x.lower() for x in accepts] 510 511 if 'application/json' in accepts: 512 return 'application/json' 513 else: 514 return ', '.join(accepts) 515 516 def select_header_content_type(self, content_types): 517 """Returns `Content-Type` based on an array of content_types provided. 518 519 :param content_types: List of content-types. 520 :return: Content-Type (e.g. application/json). 521 """ 522 if not content_types: 523 return 'application/json' 524 525 content_types = [x.lower() for x in content_types] 526 527 if 'application/json' in content_types or '*/*' in content_types: 528 return 'application/json' 529 else: 530 return content_types[0] 531 532 def update_params_for_auth(self, headers, querys, auth_settings, 533 resource_path, method, body): 534 """Updates header and query params based on authentication setting. 535 536 :param headers: Header parameters dict to be updated. 537 :param querys: Query parameters tuple list to be updated. 538 :param auth_settings: Authentication setting identifiers list. 539 :resource_path: A string representation of the HTTP request resource path. 540 :method: A string representation of the HTTP request method. 541 :body: A object representing the body of the HTTP request. 542 The object type is the return value of sanitize_for_serialization(). 543 """ 544 if not auth_settings: 545 return 546 547 for auth in auth_settings: 548 auth_setting = self.configuration.auth_settings().get(auth) 549 if auth_setting: 550 if auth_setting['in'] == 'cookie': 551 headers['Cookie'] = auth_setting['value'] 552 elif auth_setting['in'] == 'header': 553 if auth_setting['type'] != 'http-signature': 554 headers[auth_setting['key']] = auth_setting['value'] 555 {{#hasHttpSignatureMethods}} 556 else: 557 # The HTTP signature scheme requires multiple HTTP headers 558 # that are calculated dynamically. 559 signing_info = self.configuration.signing_info 560 auth_headers = signing_info.get_http_signature_headers( 561 resource_path, method, headers, body, querys) 562 headers.update(auth_headers) 563 {{/hasHttpSignatureMethods}} 564 elif auth_setting['in'] == 'query': 565 querys.append((auth_setting['key'], auth_setting['value'])) 566 else: 567 raise ApiValueError( 568 'Authentication token must be in `query` or `header`' 569 )