github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/python/api_client.mustache (about)

     1  # coding: utf-8
     2  {{>partial_header}}
     3  from __future__ import absolute_import
     4  
     5  import atexit
     6  import datetime
     7  from dateutil.parser import parse
     8  import json
     9  import mimetypes
    10  from multiprocessing.pool import ThreadPool
    11  import os
    12  import re
    13  import tempfile
    14  
    15  # python 2 and python 3 compatibility library
    16  import six
    17  from six.moves.urllib.parse import quote
    18  {{#tornado}}
    19  import tornado.gen
    20  {{/tornado}}
    21  
    22  from {{packageName}}.configuration import Configuration
    23  import {{modelPackage}}
    24  from {{packageName}} import rest
    25  from {{packageName}}.exceptions import ApiValueError
    26  
    27  
    28  class ApiClient(object):
    29      """Generic API client for OpenAPI client library builds.
    30  
    31      OpenAPI generic API client. This client handles the client-
    32      server communication, and is invariant across implementations. Specifics of
    33      the methods and models for each application are generated from the OpenAPI
    34      templates.
    35  
    36      NOTE: This class is auto generated by OpenAPI Generator.
    37      Ref: https://openapi-generator.tech
    38      Do not edit the class manually.
    39  
    40      :param configuration: .Configuration object for this client
    41      :param header_name: a header to pass when making calls to the API.
    42      :param header_value: a header value to pass when making calls to
    43          the API.
    44      :param cookie: a cookie to include in the header when making calls
    45          to the API
    46      :param pool_threads: The number of threads to use for async requests
    47          to the API. More threads means more concurrent API requests.
    48      """
    49  
    50      PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types
    51      NATIVE_TYPES_MAPPING = {
    52          'int': int,
    53          'long': int if six.PY3 else long,  # noqa: F821
    54          'float': float,
    55          'str': str,
    56          'bool': bool,
    57          'date': datetime.date,
    58          'datetime': datetime.datetime,
    59          'object': object,
    60      }
    61      _pool = None
    62  
    63      def __init__(self, configuration=None, header_name=None, header_value=None,
    64                   cookie=None, pool_threads=1):
    65          if configuration is None:
    66              configuration = Configuration.get_default_copy()
    67          self.configuration = configuration
    68          self.pool_threads = pool_threads
    69  
    70          self.rest_client = rest.RESTClientObject(configuration)
    71          self.default_headers = {}
    72          if header_name is not None:
    73              self.default_headers[header_name] = header_value
    74          self.cookie = cookie
    75          # Set default User-Agent.
    76          self.user_agent = '{{#httpUserAgent}}{{{.}}}{{/httpUserAgent}}{{^httpUserAgent}}OpenAPI-Generator/{{{packageVersion}}}/python{{/httpUserAgent}}'
    77          self.client_side_validation = configuration.client_side_validation
    78  
    79      {{#asyncio}}
    80      async def __aenter__(self):
    81          return self
    82  
    83      async def __aexit__(self, exc_type, exc_value, traceback):
    84          await self.close()
    85      {{/asyncio}}
    86      {{^asyncio}}
    87      def __enter__(self):
    88          return self
    89  
    90      def __exit__(self, exc_type, exc_value, traceback):
    91          self.close()
    92      {{/asyncio}}
    93  
    94      {{#asyncio}}async {{/asyncio}}def close(self):
    95          {{#asyncio}}
    96          await self.rest_client.close()
    97          {{/asyncio}}
    98          if self._pool:
    99              self._pool.close()
   100              self._pool.join()
   101              self._pool = None
   102              if hasattr(atexit, 'unregister'):
   103                  atexit.unregister(self.close)
   104  
   105      @property
   106      def pool(self):
   107          """Create thread pool on first request
   108           avoids instantiating unused threadpool for blocking clients.
   109          """
   110          if self._pool is None:
   111              atexit.register(self.close)
   112              self._pool = ThreadPool(self.pool_threads)
   113          return self._pool
   114  
   115      @property
   116      def user_agent(self):
   117          """User agent for this API client"""
   118          return self.default_headers['User-Agent']
   119  
   120      @user_agent.setter
   121      def user_agent(self, value):
   122          self.default_headers['User-Agent'] = value
   123  
   124      def set_default_header(self, header_name, header_value):
   125          self.default_headers[header_name] = header_value
   126  
   127      {{#tornado}}
   128      @tornado.gen.coroutine
   129      {{/tornado}}
   130      {{#asyncio}}async {{/asyncio}}def __call_api(
   131              self, resource_path, method, path_params=None,
   132              query_params=None, header_params=None, body=None, post_params=None,
   133              files=None, response_type=None, auth_settings=None,
   134              _return_http_data_only=None, collection_formats=None,
   135              _preload_content=True, _request_timeout=None, _host=None):
   136  
   137          config = self.configuration
   138  
   139          # header parameters
   140          header_params = header_params or {}
   141          header_params.update(self.default_headers)
   142          if self.cookie:
   143              header_params['Cookie'] = self.cookie
   144          if header_params:
   145              header_params = self.sanitize_for_serialization(header_params)
   146              header_params = dict(self.parameters_to_tuples(header_params,
   147                                                             collection_formats))
   148  
   149          # path parameters
   150          if path_params:
   151              path_params = self.sanitize_for_serialization(path_params)
   152              path_params = self.parameters_to_tuples(path_params,
   153                                                      collection_formats)
   154              for k, v in path_params:
   155                  # specified safe chars, encode everything
   156                  resource_path = resource_path.replace(
   157                      '{%s}' % k,
   158                      quote(str(v), safe=config.safe_chars_for_path_param)
   159                  )
   160  
   161          # query parameters
   162          if query_params:
   163              query_params = self.sanitize_for_serialization(query_params)
   164              query_params = self.parameters_to_tuples(query_params, collection_formats)
   165              query_params = self.flatten_query_params(query_params)
   166  
   167          # post parameters
   168          if post_params or files:
   169              post_params = post_params if post_params else []
   170              post_params = self.sanitize_for_serialization(post_params)
   171              post_params = self.parameters_to_tuples(post_params,
   172                                                      collection_formats)
   173              post_params.extend(self.files_parameters(files))
   174  
   175          # auth setting
   176          self.update_params_for_auth(header_params, query_params, auth_settings)
   177  
   178          # body
   179          if body:
   180              body = self.sanitize_for_serialization(body)
   181  
   182          # request url
   183          if _host is None:
   184              url = self.configuration.host + resource_path
   185          else:
   186              # use server/host defined in path or operation instead
   187              url = _host + resource_path
   188  
   189          # perform request and return response
   190          response_data = {{#asyncio}}await {{/asyncio}}{{#tornado}}yield {{/tornado}}self.request(
   191              method, url, query_params=query_params, headers=header_params,
   192              post_params=post_params, body=body,
   193              _preload_content=_preload_content,
   194              _request_timeout=_request_timeout)
   195  
   196          self.last_response = response_data
   197  
   198          return_data = response_data
   199          if _preload_content:
   200              # deserialize response data
   201              if response_type:
   202                  return_data = self.deserialize(response_data, response_type)
   203              else:
   204                  return_data = None
   205  
   206  {{^tornado}}
   207          if _return_http_data_only:
   208              return (return_data)
   209          else:
   210              return (return_data, response_data.status,
   211                      response_data.getheaders())
   212  {{/tornado}}
   213  {{#tornado}}
   214          if _return_http_data_only:
   215              raise tornado.gen.Return(return_data)
   216          else:
   217              raise tornado.gen.Return((return_data, response_data.status,
   218                                        response_data.getheaders()))
   219  {{/tornado}}
   220  
   221      def sanitize_for_serialization(self, obj):
   222          """Builds a JSON POST object.
   223  
   224          If obj is None, return None.
   225          If obj is str, int, long, float, bool, return directly.
   226          If obj is datetime.datetime, datetime.date
   227              convert to string in iso8601 format.
   228          If obj is list, sanitize each element in the list.
   229          If obj is dict, return the dict.
   230          If obj is OpenAPI model, return the properties dict.
   231  
   232          :param obj: The data to serialize.
   233          :return: The serialized form of data.
   234          """
   235          if obj is None:
   236              return None
   237          elif isinstance(obj, self.PRIMITIVE_TYPES):
   238              return obj
   239          elif isinstance(obj, list):
   240              return [self.sanitize_for_serialization(sub_obj)
   241                      for sub_obj in obj]
   242          elif isinstance(obj, tuple):
   243              return tuple(self.sanitize_for_serialization(sub_obj)
   244                           for sub_obj in obj)
   245          elif isinstance(obj, (datetime.datetime, datetime.date)):
   246              return obj.isoformat()
   247  
   248          if isinstance(obj, dict):
   249              obj_dict = obj
   250          else:
   251              # Convert model obj to dict except
   252              # attributes `openapi_types`, `attribute_map`
   253              # and attributes which value is not None.
   254              # Convert attribute name to json key in
   255              # model definition for request.
   256              obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
   257                          for attr, _ in six.iteritems(obj.openapi_types)
   258                          if getattr(obj, attr) is not None}
   259  
   260          return {key: self.sanitize_for_serialization(val)
   261                  for key, val in six.iteritems(obj_dict)}
   262  
   263      def deserialize(self, response, response_type):
   264          """Deserializes response into an object.
   265  
   266          :param response: RESTResponse object to be deserialized.
   267          :param response_type: class literal for
   268              deserialized object, or string of class name.
   269  
   270          :return: deserialized object.
   271          """
   272          # handle file downloading
   273          # save response body into a tmp file and return the instance
   274          if response_type == "bytearray":
   275              return self.__deserialize_file(response)
   276  
   277          # fetch data from response object
   278          try:
   279              data = json.loads(response.data)
   280          except ValueError:
   281              data = response.data
   282  
   283          return self.__deserialize(data, response_type)
   284  
   285      def __deserialize(self, data, klass):
   286          """Deserializes dict, list, str into an object.
   287  
   288          :param data: dict, list or str.
   289          :param klass: class literal, or string of class name.
   290  
   291          :return: object.
   292          """
   293          if data is None:
   294              return None
   295  
   296          if type(klass) == str:
   297              if klass.startswith('List['):
   298                  sub_kls = re.match(r'List\[(.*)\]', klass).group(1)
   299                  return [self.__deserialize(sub_data, sub_kls)
   300                          for sub_data in data]
   301  
   302              if klass.startswith('Dict('):
   303                  sub_kls = re.match(r'Dict\(([^,]*), (.*)\)', klass).group(2)
   304                  return {k: self.__deserialize(v, sub_kls)
   305                          for k, v in six.iteritems(data)}
   306  
   307              # convert str to class
   308              if klass in self.NATIVE_TYPES_MAPPING:
   309                  klass = self.NATIVE_TYPES_MAPPING[klass]
   310              else:
   311                  klass = getattr({{modelPackage}}, klass)
   312  
   313          if klass in self.PRIMITIVE_TYPES:
   314              return self.__deserialize_primitive(data, klass)
   315          elif klass == object:
   316              return self.__deserialize_object(data)
   317          elif klass == datetime.date:
   318              return self.__deserialize_date(data)
   319          elif klass == datetime.datetime:
   320              return self.__deserialize_datetime(data)
   321          else:
   322              return self.__deserialize_model(data, klass)
   323  
   324      def call_api(self, resource_path, method,
   325                   path_params=None, query_params=None, header_params=None,
   326                   body=None, post_params=None, files=None,
   327                   response_type=None, auth_settings=None, async_req=None,
   328                   _return_http_data_only=None, collection_formats=None,
   329                   _preload_content=True, _request_timeout=None, _host=None):
   330          """Makes the HTTP request (synchronous) and returns deserialized data.
   331  
   332          To make an async_req request, set the async_req parameter.
   333  
   334          :param resource_path: Path to method endpoint.
   335          :param method: Method to call.
   336          :param path_params: Path parameters in the url.
   337          :param query_params: Query parameters in the url.
   338          :param header_params: Header parameters to be
   339              placed in the request header.
   340          :param body: Request body.
   341          :param post_params dict: Request post form parameters,
   342              for `application/x-www-form-urlencoded`, `multipart/form-data`.
   343          :param auth_settings list: Auth Settings names for the request.
   344          :param response: Response data type.
   345          :param files dict: key -> filename, value -> filepath,
   346              for `multipart/form-data`.
   347          :param async_req bool: execute request asynchronously
   348          :param _return_http_data_only: response data without head status code
   349                                         and headers
   350          :param collection_formats: dict of collection formats for path, query,
   351              header, and post parameters.
   352          :param _preload_content: if False, the urllib3.HTTPResponse object will
   353                                   be returned without reading/decoding response
   354                                   data. Default is True.
   355          :param _request_timeout: timeout setting for this request. If one
   356                                   number provided, it will be total request
   357                                   timeout. It can also be a pair (tuple) of
   358                                   (connection, read) timeouts.
   359          :return:
   360              If async_req parameter is True,
   361              the request will be called asynchronously.
   362              The method will return the request thread.
   363              If parameter async_req is False or missing,
   364              then the method will return the response directly.
   365          """
   366          if not async_req:
   367              return self.__call_api(resource_path, method,
   368                                     path_params, query_params, header_params,
   369                                     body, post_params, files,
   370                                     response_type, auth_settings,
   371                                     _return_http_data_only, collection_formats,
   372                                     _preload_content, _request_timeout, _host)
   373  
   374          return self.pool.apply_async(self.__call_api, (resource_path,
   375                                                         method, path_params,
   376                                                         query_params,
   377                                                         header_params, body,
   378                                                         post_params, files,
   379                                                         response_type,
   380                                                         auth_settings,
   381                                                         _return_http_data_only,
   382                                                         collection_formats,
   383                                                         _preload_content,
   384                                                         _request_timeout,
   385                                                         _host))
   386  
   387      def request(self, method, url, query_params=None, headers=None,
   388                  post_params=None, body=None, _preload_content=True,
   389                  _request_timeout=None):
   390          """Makes the HTTP request using RESTClient."""
   391          if method == "GET":
   392              return self.rest_client.GET(url,
   393                                          query_params=query_params,
   394                                          _preload_content=_preload_content,
   395                                          _request_timeout=_request_timeout,
   396                                          headers=headers)
   397          elif method == "HEAD":
   398              return self.rest_client.HEAD(url,
   399                                           query_params=query_params,
   400                                           _preload_content=_preload_content,
   401                                           _request_timeout=_request_timeout,
   402                                           headers=headers)
   403          elif method == "OPTIONS":
   404              return self.rest_client.OPTIONS(url,
   405                                              query_params=query_params,
   406                                              headers=headers,
   407                                              _preload_content=_preload_content,
   408                                              _request_timeout=_request_timeout)
   409          elif method == "POST":
   410              return self.rest_client.POST(url,
   411                                           query_params=query_params,
   412                                           headers=headers,
   413                                           post_params=post_params,
   414                                           _preload_content=_preload_content,
   415                                           _request_timeout=_request_timeout,
   416                                           body=body)
   417          elif method == "PUT":
   418              return self.rest_client.PUT(url,
   419                                          query_params=query_params,
   420                                          headers=headers,
   421                                          post_params=post_params,
   422                                          _preload_content=_preload_content,
   423                                          _request_timeout=_request_timeout,
   424                                          body=body)
   425          elif method == "PATCH":
   426              return self.rest_client.PATCH(url,
   427                                            query_params=query_params,
   428                                            headers=headers,
   429                                            post_params=post_params,
   430                                            _preload_content=_preload_content,
   431                                            _request_timeout=_request_timeout,
   432                                            body=body)
   433          elif method == "DELETE":
   434              return self.rest_client.DELETE(url,
   435                                             query_params=query_params,
   436                                             headers=headers,
   437                                             _preload_content=_preload_content,
   438                                             _request_timeout=_request_timeout,
   439                                             body=body)
   440          else:
   441              raise ApiValueError(
   442                  "http method must be `GET`, `HEAD`, `OPTIONS`,"
   443                  " `POST`, `PATCH`, `PUT` or `DELETE`."
   444              )
   445  
   446      def parameters_to_tuples(self, params, collection_formats):
   447          """Get parameters as list of tuples, formatting collections.
   448  
   449          :param params: Parameters as dict or list of two-tuples
   450          :param dict collection_formats: Parameter collection formats
   451          :return: Parameters as list of tuples, collections formatted
   452          """
   453          new_params = []
   454          if collection_formats is None:
   455              collection_formats = {}
   456          for k, v in six.iteritems(params) if isinstance(params, dict) else params:  # noqa: E501
   457              if k in collection_formats:
   458                  collection_format = collection_formats[k]
   459                  if collection_format == 'multi':
   460                      new_params.extend((k, value) for value in v)
   461                  else:
   462                      if collection_format == 'ssv':
   463                          delimiter = ' '
   464                      elif collection_format == 'tsv':
   465                          delimiter = '\t'
   466                      elif collection_format == 'pipes':
   467                          delimiter = '|'
   468                      else:  # csv is the default
   469                          delimiter = ','
   470                      new_params.append(
   471                          (k, delimiter.join(str(value) for value in v)))
   472              else:
   473                  new_params.append((k, v))
   474          return new_params
   475  
   476      def flatten_query_params(self, params):
   477          result_params = {}
   478          for key, value in params:
   479              if isinstance(value, dict):
   480                  self.flatten_dict(result_params, value, key)
   481              else:
   482                  result_params[key] = value
   483  
   484          return [(key, value) for (key,value) in result_params.items()]
   485  
   486      def flatten_dict(self, params, param, namespace):
   487          for key, value in param.items():
   488              new_key = "{0}[{1}]".format(namespace, key)
   489              if isinstance(value, dict):
   490                  params = self.flatten_dict(params, value, new_key)
   491              else:
   492                  params[new_key] = value
   493  
   494          return params
   495  
   496  
   497      def files_parameters(self, files=None):
   498          """Builds form parameters.
   499  
   500          :param files: File parameters.
   501          :return: Form parameters with files.
   502          """
   503          params = []
   504  
   505          if files:
   506              for k, v in six.iteritems(files):
   507                  if not v:
   508                      continue
   509                  file_names = v if type(v) is list else [v]
   510                  for n in file_names:
   511                      with open(n, 'rb') as f:
   512                          filename = os.path.basename(f.name)
   513                          filedata = f.read()
   514                          mimetype = (mimetypes.guess_type(filename)[0] or
   515                                      'application/octet-stream')
   516                          params.append(
   517                              tuple([k, tuple([filename, filedata, mimetype])]))
   518  
   519          return params
   520  
   521      def select_header_accept(self, accepts):
   522          """Returns `Accept` based on an array of accepts provided.
   523  
   524          :param accepts: List of headers.
   525          :return: Accept (e.g. application/json).
   526          """
   527          if not accepts:
   528              return
   529  
   530          accepts = [x.lower() for x in accepts]
   531  
   532          if 'application/json' in accepts:
   533              return 'application/json'
   534          else:
   535              return ', '.join(accepts)
   536  
   537      def select_header_content_type(self, content_types):
   538          """Returns `Content-Type` based on an array of content_types provided.
   539  
   540          :param content_types: List of content-types.
   541          :return: Content-Type (e.g. application/json).
   542          """
   543          if not content_types:
   544              return 'application/json'
   545  
   546          content_types = [x.lower() for x in content_types]
   547  
   548          if 'application/json' in content_types or '*/*' in content_types:
   549              return 'application/json'
   550          else:
   551              return content_types[0]
   552  
   553      def update_params_for_auth(self, headers, querys, auth_settings):
   554          """Updates header and query params based on authentication setting.
   555  
   556          :param headers: Header parameters dict to be updated.
   557          :param querys: Query parameters tuple list to be updated.
   558          :param auth_settings: Authentication setting identifiers list.
   559          """
   560          if not auth_settings:
   561              return
   562  
   563          for auth in auth_settings:
   564              auth_setting = self.configuration.auth_settings().get(auth)
   565              if auth_setting:
   566                  if auth_setting['in'] == 'cookie':
   567                      headers['Cookie'] = auth_setting['value']
   568                  elif auth_setting['in'] == 'header':
   569                      headers[auth_setting['key']] = auth_setting['value']
   570                  elif auth_setting['in'] == 'query':
   571                      querys.append((auth_setting['key'], auth_setting['value']))
   572                  else:
   573                      raise ApiValueError(
   574                          'Authentication token must be in `query` or `header`'
   575                      )
   576  
   577      def __deserialize_file(self, response):
   578          """Deserializes body to file
   579  
   580          Saves response body into a file in a temporary folder,
   581          using the filename from the `Content-Disposition` header if provided.
   582  
   583          :param response:  RESTResponse.
   584          :return: file path.
   585          """
   586          fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path)
   587          os.close(fd)
   588          os.remove(path)
   589  
   590          content_disposition = response.getheader("Content-Disposition")
   591          if content_disposition:
   592              filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?',
   593                                   content_disposition).group(1)
   594              path = os.path.join(os.path.dirname(path), filename)
   595  
   596          # In Python 3 response.data is a string
   597          if six.PY3:
   598              with open(path, "w", encoding=response.getencoding()) as f:
   599                  f.write(response.data)
   600          else:
   601              with open(path, "wb", encoding=response.getencoding()) as f:
   602                  f.write(response.data)
   603  
   604          return path
   605  
   606      def __deserialize_primitive(self, data, klass):
   607          """Deserializes string to primitive type.
   608  
   609          :param data: str.
   610          :param klass: class literal.
   611  
   612          :return: int, long, float, str, bool.
   613          """
   614          try:
   615              return klass(data)
   616          except UnicodeEncodeError:
   617              return six.text_type(data)
   618          except TypeError:
   619              return data
   620  
   621      def __deserialize_object(self, value):
   622          """Return an original value.
   623  
   624          :return: object.
   625          """
   626          return value
   627  
   628      def __deserialize_date(self, string):
   629          """Deserializes string to date.
   630  
   631          :param string: str.
   632          :return: date.
   633          """
   634          try:
   635              return parse(string).date()
   636          except ImportError:
   637              return string
   638          except ValueError:
   639              raise rest.ApiException(
   640                  status=0,
   641                  reason="Failed to parse `{0}` as date object".format(string)
   642              )
   643  
   644      def __deserialize_datetime(self, string):
   645          """Deserializes string to datetime.
   646  
   647          The string should be in iso8601 datetime format.
   648  
   649          :param string: str.
   650          :return: datetime.
   651          """
   652          try:
   653              return parse(string)
   654          except ImportError:
   655              return string
   656          except ValueError:
   657              raise rest.ApiException(
   658                  status=0,
   659                  reason=(
   660                      "Failed to parse `{0}` as datetime object"
   661                      .format(string)
   662                  )
   663              )
   664  
   665      def __deserialize_model(self, data, klass):
   666          """Deserializes list or dict to model.
   667  
   668          :param data: dict, list.
   669          :param klass: class literal.
   670          :return: model object.
   671          """
   672  
   673          if not klass.openapi_types and not hasattr(klass,
   674                                                     'get_real_child_model'):
   675              return data
   676  
   677          kwargs = {}
   678          if (data is not None and
   679                  klass.openapi_types is not None and
   680                  isinstance(data, (list, dict))):
   681              for attr, attr_type in six.iteritems(klass.openapi_types):
   682                  if klass.attribute_map[attr] in data:
   683                      value = data[klass.attribute_map[attr]]
   684                      kwargs[attr] = self.__deserialize(value, attr_type)
   685  
   686          instance = klass(**kwargs)
   687  
   688          if hasattr(instance, 'get_real_child_model'):
   689              klass_name = instance.get_real_child_model(data)
   690              if klass_name:
   691                  instance = self.__deserialize(data, klass_name)
   692          return instance