pulp-python-client 3.12.5__py3-none-any.whl → 3.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pulp-python-client might be problematic. Click here for more details.

Files changed (68) hide show
  1. pulp_python_client-3.13.0.dist-info/METADATA +17 -0
  2. pulp_python_client-3.13.0.dist-info/RECORD +64 -0
  3. {pulp_python_client-3.12.5.dist-info → pulp_python_client-3.13.0.dist-info}/WHEEL +1 -1
  4. pulpcore/client/pulp_python/__init__.py +9 -10
  5. pulpcore/client/pulp_python/api/__init__.py +1 -2
  6. pulpcore/client/pulp_python/api/content_packages_api.py +1800 -655
  7. pulpcore/client/pulp_python/api/distributions_pypi_api.py +3755 -1474
  8. pulpcore/client/pulp_python/api/publications_pypi_api.py +2574 -982
  9. pulpcore/client/pulp_python/api/pypi_api.py +302 -128
  10. pulpcore/client/pulp_python/api/pypi_legacy_api.py +326 -153
  11. pulpcore/client/pulp_python/api/pypi_metadata_api.py +319 -139
  12. pulpcore/client/pulp_python/api/pypi_simple_api.py +899 -382
  13. pulpcore/client/pulp_python/api/remotes_python_api.py +4092 -1589
  14. pulpcore/client/pulp_python/api/repositories_python_api.py +4380 -1719
  15. pulpcore/client/pulp_python/api/repositories_python_versions_api.py +1526 -561
  16. pulpcore/client/pulp_python/api_client.py +444 -313
  17. pulpcore/client/pulp_python/api_response.py +21 -0
  18. pulpcore/client/pulp_python/configuration.py +266 -73
  19. pulpcore/client/pulp_python/exceptions.py +99 -20
  20. pulpcore/client/pulp_python/models/__init__.py +5 -8
  21. pulpcore/client/pulp_python/models/async_operation_response.py +60 -96
  22. pulpcore/client/pulp_python/models/content_summary_response.py +70 -154
  23. pulpcore/client/pulp_python/models/exclude_platforms_enum.py +19 -82
  24. pulpcore/client/pulp_python/models/my_permissions_response.py +60 -94
  25. pulpcore/client/pulp_python/models/nested_role.py +71 -155
  26. pulpcore/client/pulp_python/models/nested_role_response.py +70 -152
  27. pulpcore/client/pulp_python/models/object_roles_response.py +70 -96
  28. pulpcore/client/pulp_python/models/package_metadata_response.py +91 -191
  29. pulpcore/client/pulp_python/models/package_types_enum.py +23 -86
  30. pulpcore/client/pulp_python/models/package_upload_task_response.py +85 -154
  31. pulpcore/client/pulp_python/models/paginated_repository_version_response_list.py +94 -181
  32. pulpcore/client/pulp_python/models/paginatedpython_python_distribution_response_list.py +94 -181
  33. pulpcore/client/pulp_python/models/paginatedpython_python_package_content_response_list.py +94 -181
  34. pulpcore/client/pulp_python/models/paginatedpython_python_publication_response_list.py +94 -181
  35. pulpcore/client/pulp_python/models/paginatedpython_python_remote_response_list.py +94 -181
  36. pulpcore/client/pulp_python/models/paginatedpython_python_repository_response_list.py +94 -181
  37. pulpcore/client/pulp_python/models/patchedpython_python_distribution.py +108 -330
  38. pulpcore/client/pulp_python/models/patchedpython_python_remote.py +204 -864
  39. pulpcore/client/pulp_python/models/patchedpython_python_repository.py +96 -249
  40. pulpcore/client/pulp_python/models/policy_enum.py +18 -81
  41. pulpcore/client/pulp_python/models/python_python_distribution.py +108 -332
  42. pulpcore/client/pulp_python/models/python_python_distribution_response.py +132 -490
  43. pulpcore/client/pulp_python/models/python_python_package_content_response.py +182 -972
  44. pulpcore/client/pulp_python/models/python_python_publication.py +68 -127
  45. pulpcore/client/pulp_python/models/python_python_publication_response.py +93 -269
  46. pulpcore/client/pulp_python/models/python_python_remote.py +204 -866
  47. pulpcore/client/pulp_python/models/python_python_remote_response.py +198 -839
  48. pulpcore/client/pulp_python/models/python_python_remote_response_hidden_fields_inner.py +90 -0
  49. pulpcore/client/pulp_python/models/python_python_repository.py +96 -250
  50. pulpcore/client/pulp_python/models/python_python_repository_response.py +122 -407
  51. pulpcore/client/pulp_python/models/repair.py +60 -95
  52. pulpcore/client/pulp_python/models/repository_add_remove_content.py +71 -157
  53. pulpcore/client/pulp_python/models/repository_sync_url.py +68 -129
  54. pulpcore/client/pulp_python/models/repository_version_response.py +104 -296
  55. pulpcore/client/pulp_python/models/set_label.py +80 -130
  56. pulpcore/client/pulp_python/models/set_label_response.py +80 -127
  57. pulpcore/client/pulp_python/models/summary_response.py +70 -160
  58. pulpcore/client/pulp_python/models/unset_label.py +75 -107
  59. pulpcore/client/pulp_python/models/unset_label_response.py +78 -129
  60. pulpcore/client/pulp_python/py.typed +0 -0
  61. pulpcore/client/pulp_python/rest.py +136 -170
  62. pulp_python_client-3.12.5.dist-info/METADATA +0 -17
  63. pulp_python_client-3.12.5.dist-info/RECORD +0 -65
  64. pulpcore/client/pulp_python/models/package_upload.py +0 -190
  65. pulpcore/client/pulp_python/models/python_bander_remote.py +0 -184
  66. pulpcore/client/pulp_python/models/python_python_package_content.py +0 -889
  67. pulpcore/client/pulp_python/models/python_python_remote_response_hidden_fields.py +0 -149
  68. {pulp_python_client-3.12.5.dist-info → pulp_python_client-3.13.0.dist-info}/top_level.txt +0 -0
@@ -1,37 +1,49 @@
1
1
  # coding: utf-8
2
+
2
3
  """
3
4
  Pulp 3 API
4
5
 
5
- Fetch, Upload, Organize, and Distribute Software Packages # noqa: E501
6
+ Fetch, Upload, Organize, and Distribute Software Packages
6
7
 
7
8
  The version of the OpenAPI document: v3
8
9
  Contact: pulp-list@redhat.com
9
- Generated by: https://openapi-generator.tech
10
- """
10
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
11
+
12
+ Do not edit the class manually.
13
+ """ # noqa: E501
11
14
 
12
- from __future__ import absolute_import
13
15
 
14
- import atexit
15
16
  import datetime
16
17
  from dateutil.parser import parse
18
+ from enum import Enum
19
+ import decimal
17
20
  import json
18
21
  import mimetypes
19
- from multiprocessing.pool import ThreadPool
20
22
  import os
21
23
  import re
22
24
  import tempfile
23
25
 
24
- # python 2 and python 3 compatibility library
25
- import six
26
- from six.moves.urllib.parse import quote
26
+ from urllib.parse import quote
27
+ from typing import Tuple, Optional, List, Dict, Union
28
+ from pydantic import SecretStr
27
29
 
28
30
  from pulpcore.client.pulp_python.configuration import Configuration
31
+ from pulpcore.client.pulp_python.api_response import ApiResponse, T as ApiResponseT
29
32
  import pulpcore.client.pulp_python.models
30
33
  from pulpcore.client.pulp_python import rest
31
- from pulpcore.client.pulp_python.exceptions import ApiValueError, ApiException
32
-
33
-
34
- class ApiClient(object):
34
+ from pulpcore.client.pulp_python.exceptions import (
35
+ ApiValueError,
36
+ ApiException,
37
+ BadRequestException,
38
+ UnauthorizedException,
39
+ ForbiddenException,
40
+ NotFoundException,
41
+ ServiceException
42
+ )
43
+
44
+ RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]]
45
+
46
+ class ApiClient:
35
47
  """Generic API client for OpenAPI client library builds.
36
48
 
37
49
  OpenAPI generic API client. This client handles the client-
@@ -39,39 +51,39 @@ class ApiClient(object):
39
51
  the methods and models for each application are generated from the OpenAPI
40
52
  templates.
41
53
 
42
- NOTE: This class is auto generated by OpenAPI Generator.
43
- Ref: https://openapi-generator.tech
44
- Do not edit the class manually.
45
-
46
54
  :param configuration: .Configuration object for this client
47
55
  :param header_name: a header to pass when making calls to the API.
48
56
  :param header_value: a header value to pass when making calls to
49
57
  the API.
50
58
  :param cookie: a cookie to include in the header when making calls
51
59
  to the API
52
- :param pool_threads: The number of threads to use for async requests
53
- to the API. More threads means more concurrent API requests.
54
60
  """
55
61
 
56
- PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types
62
+ PRIMITIVE_TYPES = (float, bool, bytes, str, int)
57
63
  NATIVE_TYPES_MAPPING = {
58
64
  'int': int,
59
- 'long': int if six.PY3 else long, # noqa: F821
65
+ 'long': int, # TODO remove as only py3 is supported?
60
66
  'float': float,
61
67
  'str': str,
62
68
  'bool': bool,
63
69
  'date': datetime.date,
64
70
  'datetime': datetime.datetime,
71
+ 'decimal': decimal.Decimal,
65
72
  'object': object,
66
73
  }
67
74
  _pool = None
68
75
 
69
- def __init__(self, configuration=None, header_name=None, header_value=None,
70
- cookie=None, pool_threads=1):
76
+ def __init__(
77
+ self,
78
+ configuration=None,
79
+ header_name=None,
80
+ header_value=None,
81
+ cookie=None
82
+ ) -> None:
83
+ # use default configuration if none is provided
71
84
  if configuration is None:
72
- configuration = Configuration.get_default_copy()
85
+ configuration = Configuration.get_default()
73
86
  self.configuration = configuration
74
- self.pool_threads = pool_threads
75
87
 
76
88
  self.rest_client = rest.RESTClientObject(configuration)
77
89
  self.default_headers = {}
@@ -79,32 +91,14 @@ class ApiClient(object):
79
91
  self.default_headers[header_name] = header_value
80
92
  self.cookie = cookie
81
93
  # Set default User-Agent.
82
- self.user_agent = 'OpenAPI-Generator/3.12.5/python'
94
+ self.user_agent = 'OpenAPI-Generator/3.13.0/python'
83
95
  self.client_side_validation = configuration.client_side_validation
84
96
 
85
97
  def __enter__(self):
86
98
  return self
87
99
 
88
100
  def __exit__(self, exc_type, exc_value, traceback):
89
- self.close()
90
-
91
- def close(self):
92
- if self._pool:
93
- self._pool.close()
94
- self._pool.join()
95
- self._pool = None
96
- if hasattr(atexit, 'unregister'):
97
- atexit.unregister(self.close)
98
-
99
- @property
100
- def pool(self):
101
- """Create thread pool on first request
102
- avoids instantiating unused threadpool for blocking clients.
103
- """
104
- if self._pool is None:
105
- atexit.register(self.close)
106
- self._pool = ThreadPool(self.pool_threads)
107
- return self._pool
101
+ pass
108
102
 
109
103
  @property
110
104
  def user_agent(self):
@@ -118,12 +112,69 @@ class ApiClient(object):
118
112
  def set_default_header(self, header_name, header_value):
119
113
  self.default_headers[header_name] = header_value
120
114
 
121
- def __call_api(
122
- self, resource_path, method, path_params=None,
123
- query_params=None, header_params=None, body=None, post_params=None,
124
- files=None, response_type=None, auth_settings=None,
125
- _return_http_data_only=None, collection_formats=None,
126
- _preload_content=True, _request_timeout=None, _host=None):
115
+
116
+ _default = None
117
+
118
+ @classmethod
119
+ def get_default(cls):
120
+ """Return new instance of ApiClient.
121
+
122
+ This method returns newly created, based on default constructor,
123
+ object of ApiClient class or returns a copy of default
124
+ ApiClient.
125
+
126
+ :return: The ApiClient object.
127
+ """
128
+ if cls._default is None:
129
+ cls._default = ApiClient()
130
+ return cls._default
131
+
132
+ @classmethod
133
+ def set_default(cls, default):
134
+ """Set default instance of ApiClient.
135
+
136
+ It stores default ApiClient.
137
+
138
+ :param default: object of ApiClient.
139
+ """
140
+ cls._default = default
141
+
142
+ def param_serialize(
143
+ self,
144
+ method,
145
+ resource_path,
146
+ path_params=None,
147
+ query_params=None,
148
+ header_params=None,
149
+ body=None,
150
+ post_params=None,
151
+ files=None, auth_settings=None,
152
+ collection_formats=None,
153
+ _host=None,
154
+ _request_auth=None
155
+ ) -> RequestSerialized:
156
+
157
+ """Builds the HTTP request params needed by the request.
158
+ :param method: Method to call.
159
+ :param resource_path: Path to method endpoint.
160
+ :param path_params: Path parameters in the url.
161
+ :param query_params: Query parameters in the url.
162
+ :param header_params: Header parameters to be
163
+ placed in the request header.
164
+ :param body: Request body.
165
+ :param post_params dict: Request post form parameters,
166
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
167
+ :param auth_settings list: Auth Settings names for the request.
168
+ :param files dict: key -> filename, value -> filepath,
169
+ for `multipart/form-data`.
170
+ :param collection_formats: dict of collection formats for path, query,
171
+ header, and post parameters.
172
+ :param _request_auth: set to override the auth_settings for an a single
173
+ request; this effectively ignores the authentication
174
+ in the spec for a single request.
175
+ :return: tuple of form (path, http_method, query_params, header_params,
176
+ body, post_params, files)
177
+ """
127
178
 
128
179
  config = self.configuration
129
180
 
@@ -134,14 +185,17 @@ class ApiClient(object):
134
185
  header_params['Cookie'] = self.cookie
135
186
  if header_params:
136
187
  header_params = self.sanitize_for_serialization(header_params)
137
- header_params = dict(self.parameters_to_tuples(header_params,
138
- collection_formats))
188
+ header_params = dict(
189
+ self.parameters_to_tuples(header_params,collection_formats)
190
+ )
139
191
 
140
192
  # path parameters
141
193
  if path_params:
142
194
  path_params = self.sanitize_for_serialization(path_params)
143
- path_params = self.parameters_to_tuples(path_params,
144
- collection_formats)
195
+ path_params = self.parameters_to_tuples(
196
+ path_params,
197
+ collection_formats
198
+ )
145
199
  for k, v in path_params:
146
200
  # specified safe chars, encode everything
147
201
  resource_path = resource_path.replace(
@@ -149,80 +203,145 @@ class ApiClient(object):
149
203
  quote(str(v), safe=config.safe_chars_for_path_param)
150
204
  )
151
205
 
152
- # query parameters
153
- if query_params:
154
- query_params = self.sanitize_for_serialization(query_params)
155
- query_params = self.parameters_to_tuples(query_params,
156
- collection_formats)
157
-
158
206
  # post parameters
159
207
  if post_params or files:
160
208
  post_params = post_params if post_params else []
161
209
  post_params = self.sanitize_for_serialization(post_params)
162
- post_params = self.parameters_to_tuples(post_params,
163
- collection_formats)
164
- post_params.extend(self.files_parameters(files))
210
+ post_params = self.parameters_to_tuples(
211
+ post_params,
212
+ collection_formats
213
+ )
214
+ if files:
215
+ post_params.extend(self.files_parameters(files))
165
216
 
166
217
  # auth setting
167
- self.update_params_for_auth(header_params, query_params, auth_settings)
218
+ self.update_params_for_auth(
219
+ header_params,
220
+ query_params,
221
+ auth_settings,
222
+ resource_path,
223
+ method,
224
+ body,
225
+ request_auth=_request_auth
226
+ )
168
227
 
169
228
  # body
170
229
  if body:
171
230
  body = self.sanitize_for_serialization(body)
172
231
 
173
232
  # request url
174
- if _host is None:
233
+ if _host is None or self.configuration.ignore_operation_servers:
175
234
  url = self.configuration.host + resource_path
176
235
  else:
177
236
  # use server/host defined in path or operation instead
178
237
  url = _host + resource_path
179
238
 
239
+ # query parameters
240
+ if query_params:
241
+ query_params = self.sanitize_for_serialization(query_params)
242
+ url_query = self.parameters_to_url_query(
243
+ query_params,
244
+ collection_formats
245
+ )
246
+ url += "?" + url_query
247
+
248
+ return method, url, header_params, body, post_params
249
+
250
+
251
+ def call_api(
252
+ self,
253
+ method,
254
+ url,
255
+ header_params=None,
256
+ body=None,
257
+ post_params=None,
258
+ _request_timeout=None
259
+ ) -> rest.RESTResponse:
260
+ """Makes the HTTP request (synchronous)
261
+ :param method: Method to call.
262
+ :param url: Path to method endpoint.
263
+ :param header_params: Header parameters to be
264
+ placed in the request header.
265
+ :param body: Request body.
266
+ :param post_params dict: Request post form parameters,
267
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
268
+ :param _request_timeout: timeout setting for this request.
269
+ :return: RESTResponse
270
+ """
271
+
180
272
  try:
181
273
  # perform request and return response
182
- response_data = self.request(
183
- method, url, query_params=query_params, headers=header_params,
184
- post_params=post_params, body=body,
185
- _preload_content=_preload_content,
186
- _request_timeout=_request_timeout)
274
+ response_data = self.rest_client.request(
275
+ method, url,
276
+ headers=header_params,
277
+ body=body, post_params=post_params,
278
+ _request_timeout=_request_timeout
279
+ )
280
+
187
281
  except ApiException as e:
188
- e.body = e.body.decode('utf-8') if six.PY3 else e.body
189
282
  raise e
190
283
 
191
- content_type = response_data.getheader('content-type')
284
+ return response_data
192
285
 
193
- self.last_response = response_data
194
-
195
- return_data = response_data
286
+ def response_deserialize(
287
+ self,
288
+ response_data: rest.RESTResponse,
289
+ response_types_map: Optional[Dict[str, ApiResponseT]]=None
290
+ ) -> ApiResponse[ApiResponseT]:
291
+ """Deserializes response into an object.
292
+ :param response_data: RESTResponse object to be deserialized.
293
+ :param response_types_map: dict of response types.
294
+ :return: ApiResponse
295
+ """
196
296
 
197
- if not _preload_content:
198
- return return_data
297
+ msg = "RESTResponse.read() must be called before passing it to response_deserialize()"
298
+ assert response_data.data is not None, msg
199
299
 
200
- if six.PY3 and response_type not in ["file", "bytes"]:
201
- match = None
202
- if content_type is not None:
203
- match = re.search(r"charset=([a-zA-Z\-\d]+)[\s\;]?", content_type)
204
- encoding = match.group(1) if match else "utf-8"
205
- response_data.data = response_data.data.decode(encoding)
300
+ response_type = response_types_map.get(str(response_data.status), None)
301
+ if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599:
302
+ # if not found, look for '1XX', '2XX', etc.
303
+ response_type = response_types_map.get(str(response_data.status)[0] + "XX", None)
206
304
 
207
305
  # deserialize response data
208
- if response_type:
209
- return_data = self.deserialize(response_data, response_type)
210
- else:
211
- return_data = None
306
+ response_text = None
307
+ return_data = None
308
+ try:
309
+ if response_type == "bytearray":
310
+ return_data = response_data.data
311
+ elif response_type == "file":
312
+ return_data = self.__deserialize_file(response_data)
313
+ elif response_type is not None:
314
+ match = None
315
+ content_type = response_data.getheader('content-type')
316
+ if content_type is not None:
317
+ match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type)
318
+ encoding = match.group(1) if match else "utf-8"
319
+ response_text = response_data.data.decode(encoding)
320
+ return_data = self.deserialize(response_text, response_type, content_type)
321
+ finally:
322
+ if not 200 <= response_data.status <= 299:
323
+ raise ApiException.from_response(
324
+ http_resp=response_data,
325
+ body=response_text,
326
+ data=return_data,
327
+ )
212
328
 
213
- if _return_http_data_only:
214
- return (return_data)
215
- else:
216
- return (return_data, response_data.status,
217
- response_data.getheaders())
329
+ return ApiResponse(
330
+ status_code = response_data.status,
331
+ data = return_data,
332
+ headers = response_data.getheaders(),
333
+ raw_data = response_data.data
334
+ )
218
335
 
219
336
  def sanitize_for_serialization(self, obj):
220
337
  """Builds a JSON POST object.
221
338
 
222
339
  If obj is None, return None.
340
+ If obj is SecretStr, return obj.get_secret_value()
223
341
  If obj is str, int, long, float, bool, return directly.
224
342
  If obj is datetime.datetime, datetime.date
225
343
  convert to string in iso8601 format.
344
+ If obj is decimal.Decimal return string representation.
226
345
  If obj is list, sanitize each element in the list.
227
346
  If obj is dict, return the dict.
228
347
  If obj is OpenAPI model, return the properties dict.
@@ -232,18 +351,26 @@ class ApiClient(object):
232
351
  """
233
352
  if obj is None:
234
353
  return None
354
+ elif isinstance(obj, Enum):
355
+ return obj.value
356
+ elif isinstance(obj, SecretStr):
357
+ return obj.get_secret_value()
235
358
  elif isinstance(obj, self.PRIMITIVE_TYPES):
236
359
  return obj
237
360
  elif isinstance(obj, list):
238
- return [self.sanitize_for_serialization(sub_obj)
239
- for sub_obj in obj]
361
+ return [
362
+ self.sanitize_for_serialization(sub_obj) for sub_obj in obj
363
+ ]
240
364
  elif isinstance(obj, tuple):
241
- return tuple(self.sanitize_for_serialization(sub_obj)
242
- for sub_obj in obj)
365
+ return tuple(
366
+ self.sanitize_for_serialization(sub_obj) for sub_obj in obj
367
+ )
243
368
  elif isinstance(obj, (datetime.datetime, datetime.date)):
244
369
  return obj.isoformat()
370
+ elif isinstance(obj, decimal.Decimal):
371
+ return str(obj)
245
372
 
246
- if isinstance(obj, dict):
373
+ elif isinstance(obj, dict):
247
374
  obj_dict = obj
248
375
  else:
249
376
  # Convert model obj to dict except
@@ -251,32 +378,45 @@ class ApiClient(object):
251
378
  # and attributes which value is not None.
252
379
  # Convert attribute name to json key in
253
380
  # model definition for request.
254
- obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
255
- for attr, _ in six.iteritems(obj.openapi_types)
256
- if getattr(obj, attr) is not None}
381
+ if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
382
+ obj_dict = obj.to_dict()
383
+ else:
384
+ obj_dict = obj.__dict__
257
385
 
258
- return {key: self.sanitize_for_serialization(val)
259
- for key, val in six.iteritems(obj_dict)}
386
+ return {
387
+ key: self.sanitize_for_serialization(val)
388
+ for key, val in obj_dict.items()
389
+ }
260
390
 
261
- def deserialize(self, response, response_type):
391
+ def deserialize(self, response_text: str, response_type: str, content_type: Optional[str]):
262
392
  """Deserializes response into an object.
263
393
 
264
394
  :param response: RESTResponse object to be deserialized.
265
395
  :param response_type: class literal for
266
396
  deserialized object, or string of class name.
397
+ :param content_type: content type of response.
267
398
 
268
399
  :return: deserialized object.
269
400
  """
270
- # handle file downloading
271
- # save response body into a tmp file and return the instance
272
- if response_type == "file":
273
- return self.__deserialize_file(response)
274
401
 
275
402
  # fetch data from response object
276
- try:
277
- data = json.loads(response.data)
278
- except ValueError:
279
- data = response.data
403
+ if content_type is None:
404
+ try:
405
+ data = json.loads(response_text)
406
+ except ValueError:
407
+ data = response_text
408
+ elif re.match(r'^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)', content_type, re.IGNORECASE):
409
+ if response_text == "":
410
+ data = ""
411
+ else:
412
+ data = json.loads(response_text)
413
+ elif re.match(r'^text\/[a-z.+-]+\s*(;|$)', content_type, re.IGNORECASE):
414
+ data = response_text
415
+ else:
416
+ raise ApiException(
417
+ status=0,
418
+ reason="Unsupported content type: {0}".format(content_type)
419
+ )
280
420
 
281
421
  return self.__deserialize(data, response_type)
282
422
 
@@ -291,16 +431,20 @@ class ApiClient(object):
291
431
  if data is None:
292
432
  return None
293
433
 
294
- if type(klass) == str:
295
- if klass.startswith('list['):
296
- sub_kls = re.match(r'list\[(.*)\]', klass).group(1)
434
+ if isinstance(klass, str):
435
+ if klass.startswith('List['):
436
+ m = re.match(r'List\[(.*)]', klass)
437
+ assert m is not None, "Malformed List type definition"
438
+ sub_kls = m.group(1)
297
439
  return [self.__deserialize(sub_data, sub_kls)
298
440
  for sub_data in data]
299
441
 
300
- if klass.startswith('dict('):
301
- sub_kls = re.match(r'dict\(([^,]*), (.*)\)', klass).group(2)
442
+ if klass.startswith('Dict['):
443
+ m = re.match(r'Dict\[([^,]*), (.*)]', klass)
444
+ assert m is not None, "Malformed Dict type definition"
445
+ sub_kls = m.group(2)
302
446
  return {k: self.__deserialize(v, sub_kls)
303
- for k, v in six.iteritems(data)}
447
+ for k, v in data.items()}
304
448
 
305
449
  # convert str to class
306
450
  if klass in self.NATIVE_TYPES_MAPPING:
@@ -316,131 +460,13 @@ class ApiClient(object):
316
460
  return self.__deserialize_date(data)
317
461
  elif klass == datetime.datetime:
318
462
  return self.__deserialize_datetime(data)
463
+ elif klass == decimal.Decimal:
464
+ return decimal.Decimal(data)
465
+ elif issubclass(klass, Enum):
466
+ return self.__deserialize_enum(data, klass)
319
467
  else:
320
468
  return self.__deserialize_model(data, klass)
321
469
 
322
- def call_api(self, resource_path, method,
323
- path_params=None, query_params=None, header_params=None,
324
- body=None, post_params=None, files=None,
325
- response_type=None, auth_settings=None, async_req=None,
326
- _return_http_data_only=None, collection_formats=None,
327
- _preload_content=True, _request_timeout=None, _host=None):
328
- """Makes the HTTP request (synchronous) and returns deserialized data.
329
-
330
- To make an async_req request, set the async_req parameter.
331
-
332
- :param resource_path: Path to method endpoint.
333
- :param method: Method to call.
334
- :param path_params: Path parameters in the url.
335
- :param query_params: Query parameters in the url.
336
- :param header_params: Header parameters to be
337
- placed in the request header.
338
- :param body: Request body.
339
- :param post_params dict: Request post form parameters,
340
- for `application/x-www-form-urlencoded`, `multipart/form-data`.
341
- :param auth_settings list: Auth Settings names for the request.
342
- :param response: Response data type.
343
- :param files dict: key -> filename, value -> filepath,
344
- for `multipart/form-data`.
345
- :param async_req bool: execute request asynchronously
346
- :param _return_http_data_only: response data without head status code
347
- and headers
348
- :param collection_formats: dict of collection formats for path, query,
349
- header, and post parameters.
350
- :param _preload_content: if False, the urllib3.HTTPResponse object will
351
- be returned without reading/decoding response
352
- data. Default is True.
353
- :param _request_timeout: timeout setting for this request. If one
354
- number provided, it will be total request
355
- timeout. It can also be a pair (tuple) of
356
- (connection, read) timeouts.
357
- :return:
358
- If async_req parameter is True,
359
- the request will be called asynchronously.
360
- The method will return the request thread.
361
- If parameter async_req is False or missing,
362
- then the method will return the response directly.
363
- """
364
- if not async_req:
365
- return self.__call_api(resource_path, method,
366
- path_params, query_params, header_params,
367
- body, post_params, files,
368
- response_type, auth_settings,
369
- _return_http_data_only, collection_formats,
370
- _preload_content, _request_timeout, _host)
371
-
372
- return self.pool.apply_async(self.__call_api, (resource_path,
373
- method, path_params,
374
- query_params,
375
- header_params, body,
376
- post_params, files,
377
- response_type,
378
- auth_settings,
379
- _return_http_data_only,
380
- collection_formats,
381
- _preload_content,
382
- _request_timeout,
383
- _host))
384
-
385
- def request(self, method, url, query_params=None, headers=None,
386
- post_params=None, body=None, _preload_content=True,
387
- _request_timeout=None):
388
- """Makes the HTTP request using RESTClient."""
389
- if method == "GET":
390
- return self.rest_client.GET(url,
391
- query_params=query_params,
392
- _preload_content=_preload_content,
393
- _request_timeout=_request_timeout,
394
- headers=headers)
395
- elif method == "HEAD":
396
- return self.rest_client.HEAD(url,
397
- query_params=query_params,
398
- _preload_content=_preload_content,
399
- _request_timeout=_request_timeout,
400
- headers=headers)
401
- elif method == "OPTIONS":
402
- return self.rest_client.OPTIONS(url,
403
- query_params=query_params,
404
- headers=headers,
405
- _preload_content=_preload_content,
406
- _request_timeout=_request_timeout)
407
- elif method == "POST":
408
- return self.rest_client.POST(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 == "PUT":
416
- return self.rest_client.PUT(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 == "PATCH":
424
- return self.rest_client.PATCH(url,
425
- query_params=query_params,
426
- headers=headers,
427
- post_params=post_params,
428
- _preload_content=_preload_content,
429
- _request_timeout=_request_timeout,
430
- body=body)
431
- elif method == "DELETE":
432
- return self.rest_client.DELETE(url,
433
- query_params=query_params,
434
- headers=headers,
435
- _preload_content=_preload_content,
436
- _request_timeout=_request_timeout,
437
- body=body)
438
- else:
439
- raise ApiValueError(
440
- "http method must be `GET`, `HEAD`, `OPTIONS`,"
441
- " `POST`, `PATCH`, `PUT` or `DELETE`."
442
- )
443
-
444
470
  def parameters_to_tuples(self, params, collection_formats):
445
471
  """Get parameters as list of tuples, formatting collections.
446
472
 
@@ -448,10 +474,10 @@ class ApiClient(object):
448
474
  :param dict collection_formats: Parameter collection formats
449
475
  :return: Parameters as list of tuples, collections formatted
450
476
  """
451
- new_params = []
477
+ new_params: List[Tuple[str, str]] = []
452
478
  if collection_formats is None:
453
479
  collection_formats = {}
454
- for k, v in six.iteritems(params) if isinstance(params, dict) else params: # noqa: E501
480
+ for k, v in params.items() if isinstance(params, dict) else params:
455
481
  if k in collection_formats:
456
482
  collection_format = collection_formats[k]
457
483
  if collection_format == 'multi':
@@ -471,45 +497,94 @@ class ApiClient(object):
471
497
  new_params.append((k, v))
472
498
  return new_params
473
499
 
474
- def files_parameters(self, files=None):
500
+ def parameters_to_url_query(self, params, collection_formats):
501
+ """Get parameters as list of tuples, formatting collections.
502
+
503
+ :param params: Parameters as dict or list of two-tuples
504
+ :param dict collection_formats: Parameter collection formats
505
+ :return: URL query string (e.g. a=Hello%20World&b=123)
506
+ """
507
+ new_params: List[Tuple[str, str]] = []
508
+ if collection_formats is None:
509
+ collection_formats = {}
510
+ for k, v in params.items() if isinstance(params, dict) else params:
511
+ if isinstance(v, bool):
512
+ v = str(v).lower()
513
+ if isinstance(v, (int, float)):
514
+ v = str(v)
515
+ if isinstance(v, dict):
516
+ v = json.dumps(v)
517
+
518
+ if k in collection_formats:
519
+ collection_format = collection_formats[k]
520
+ if collection_format == 'multi':
521
+ new_params.extend((k, str(value)) for value in v)
522
+ else:
523
+ if collection_format == 'ssv':
524
+ delimiter = ' '
525
+ elif collection_format == 'tsv':
526
+ delimiter = '\t'
527
+ elif collection_format == 'pipes':
528
+ delimiter = '|'
529
+ else: # csv is the default
530
+ delimiter = ','
531
+ new_params.append(
532
+ (k, delimiter.join(quote(str(value)) for value in v))
533
+ )
534
+ else:
535
+ new_params.append((k, quote(str(v))))
536
+
537
+ return "&".join(["=".join(map(str, item)) for item in new_params])
538
+
539
+ def files_parameters(
540
+ self,
541
+ files: Dict[str, Union[str, bytes, List[str], List[bytes], Tuple[str, bytes]]],
542
+ ):
475
543
  """Builds form parameters.
476
544
 
477
545
  :param files: File parameters.
478
546
  :return: Form parameters with files.
479
547
  """
480
548
  params = []
481
-
482
- if files:
483
- for k, v in six.iteritems(files):
484
- if not v:
485
- continue
486
- file_names = v if type(v) is list else [v]
487
- for n in file_names:
488
- with open(n, 'rb') as f:
489
- filename = os.path.basename(f.name)
490
- filedata = f.read()
491
- mimetype = (mimetypes.guess_type(filename)[0] or
492
- 'application/octet-stream')
493
- params.append(
494
- tuple([k, tuple([filename, filedata, mimetype])]))
495
-
549
+ for k, v in files.items():
550
+ if isinstance(v, str):
551
+ with open(v, 'rb') as f:
552
+ filename = os.path.basename(f.name)
553
+ filedata = f.read()
554
+ elif isinstance(v, bytes):
555
+ filename = k
556
+ filedata = v
557
+ elif isinstance(v, tuple):
558
+ filename, filedata = v
559
+ elif isinstance(v, list):
560
+ for file_param in v:
561
+ params.extend(self.files_parameters({k: file_param}))
562
+ continue
563
+ else:
564
+ raise ValueError("Unsupported file value")
565
+ mimetype = (
566
+ mimetypes.guess_type(filename)[0]
567
+ or 'application/octet-stream'
568
+ )
569
+ params.append(
570
+ tuple([k, tuple([filename, filedata, mimetype])])
571
+ )
496
572
  return params
497
573
 
498
- def select_header_accept(self, accepts):
574
+ def select_header_accept(self, accepts: List[str]) -> Optional[str]:
499
575
  """Returns `Accept` based on an array of accepts provided.
500
576
 
501
577
  :param accepts: List of headers.
502
578
  :return: Accept (e.g. application/json).
503
579
  """
504
580
  if not accepts:
505
- return
581
+ return None
506
582
 
507
- accepts = [x.lower() for x in accepts]
583
+ for accept in accepts:
584
+ if re.search('json', accept, re.IGNORECASE):
585
+ return accept
508
586
 
509
- if 'application/json' in accepts:
510
- return 'application/json'
511
- else:
512
- return ', '.join(accepts)
587
+ return accepts[0]
513
588
 
514
589
  def select_header_content_type(self, content_types):
515
590
  """Returns `Content-Type` based on an array of content_types provided.
@@ -518,45 +593,101 @@ class ApiClient(object):
518
593
  :return: Content-Type (e.g. application/json).
519
594
  """
520
595
  if not content_types:
521
- return 'application/json'
522
-
523
- content_types = [x.lower() for x in content_types]
524
-
525
- if 'application/json' in content_types or '*/*' in content_types:
526
- return 'application/json'
527
- else:
528
- return content_types[0]
596
+ return None
529
597
 
530
- def update_params_for_auth(self, headers, querys, auth_settings):
598
+ for content_type in content_types:
599
+ if re.search('json', content_type, re.IGNORECASE):
600
+ return content_type
601
+
602
+ return content_types[0]
603
+
604
+ def update_params_for_auth(
605
+ self,
606
+ headers,
607
+ queries,
608
+ auth_settings,
609
+ resource_path,
610
+ method,
611
+ body,
612
+ request_auth=None
613
+ ) -> None:
531
614
  """Updates header and query params based on authentication setting.
532
615
 
533
616
  :param headers: Header parameters dict to be updated.
534
- :param querys: Query parameters tuple list to be updated.
617
+ :param queries: Query parameters tuple list to be updated.
535
618
  :param auth_settings: Authentication setting identifiers list.
619
+ :resource_path: A string representation of the HTTP request resource path.
620
+ :method: A string representation of the HTTP request method.
621
+ :body: A object representing the body of the HTTP request.
622
+ The object type is the return value of sanitize_for_serialization().
623
+ :param request_auth: if set, the provided settings will
624
+ override the token in the configuration.
536
625
  """
537
626
  if not auth_settings:
538
627
  return
539
628
 
540
- for auth in auth_settings:
541
- auth_setting = self.configuration.auth_settings().get(auth)
542
- if auth_setting:
543
- if auth_setting['in'] == 'cookie':
544
- headers['Cookie'] = auth_setting['value']
545
- elif auth_setting['in'] == 'header':
546
- headers[auth_setting['key']] = auth_setting['value']
547
- elif auth_setting['in'] == 'query':
548
- querys.append((auth_setting['key'], auth_setting['value']))
549
- else:
550
- raise ApiValueError(
551
- 'Authentication token must be in `query` or `header`'
629
+ if request_auth:
630
+ self._apply_auth_params(
631
+ headers,
632
+ queries,
633
+ resource_path,
634
+ method,
635
+ body,
636
+ request_auth
637
+ )
638
+ else:
639
+ for auth in auth_settings:
640
+ auth_setting = self.configuration.auth_settings().get(auth)
641
+ if auth_setting:
642
+ self._apply_auth_params(
643
+ headers,
644
+ queries,
645
+ resource_path,
646
+ method,
647
+ body,
648
+ auth_setting
552
649
  )
553
650
 
651
+ def _apply_auth_params(
652
+ self,
653
+ headers,
654
+ queries,
655
+ resource_path,
656
+ method,
657
+ body,
658
+ auth_setting
659
+ ) -> None:
660
+ """Updates the request parameters based on a single auth_setting
661
+
662
+ :param headers: Header parameters dict to be updated.
663
+ :param queries: Query parameters tuple list to be updated.
664
+ :resource_path: A string representation of the HTTP request resource path.
665
+ :method: A string representation of the HTTP request method.
666
+ :body: A object representing the body of the HTTP request.
667
+ The object type is the return value of sanitize_for_serialization().
668
+ :param auth_setting: auth settings for the endpoint
669
+ """
670
+ if auth_setting['in'] == 'cookie':
671
+ headers['Cookie'] = auth_setting['value']
672
+ elif auth_setting['in'] == 'header':
673
+ if auth_setting['type'] != 'http-signature':
674
+ headers[auth_setting['key']] = auth_setting['value']
675
+ elif auth_setting['in'] == 'query':
676
+ queries.append((auth_setting['key'], auth_setting['value']))
677
+ else:
678
+ raise ApiValueError(
679
+ 'Authentication token must be in `query` or `header`'
680
+ )
681
+
554
682
  def __deserialize_file(self, response):
555
683
  """Deserializes body to file
556
684
 
557
685
  Saves response body into a file in a temporary folder,
558
686
  using the filename from the `Content-Disposition` header if provided.
559
687
 
688
+ handle file downloading
689
+ save response body into a tmp file and return the instance
690
+
560
691
  :param response: RESTResponse.
561
692
  :return: file path.
562
693
  """
@@ -566,8 +697,12 @@ class ApiClient(object):
566
697
 
567
698
  content_disposition = response.getheader("Content-Disposition")
568
699
  if content_disposition:
569
- filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?',
570
- content_disposition).group(1)
700
+ m = re.search(
701
+ r'filename=[\'"]?([^\'"\s]+)[\'"]?',
702
+ content_disposition
703
+ )
704
+ assert m is not None, "Unexpected 'content-disposition' header value"
705
+ filename = m.group(1)
571
706
  path = os.path.join(os.path.dirname(path), filename)
572
707
 
573
708
  with open(path, "wb") as f:
@@ -586,7 +721,7 @@ class ApiClient(object):
586
721
  try:
587
722
  return klass(data)
588
723
  except UnicodeEncodeError:
589
- return six.text_type(data)
724
+ return str(data)
590
725
  except TypeError:
591
726
  return data
592
727
 
@@ -634,6 +769,24 @@ class ApiClient(object):
634
769
  )
635
770
  )
636
771
 
772
+ def __deserialize_enum(self, data, klass):
773
+ """Deserializes primitive type to enum.
774
+
775
+ :param data: primitive type.
776
+ :param klass: class literal.
777
+ :return: enum value.
778
+ """
779
+ try:
780
+ return klass(data)
781
+ except ValueError:
782
+ raise rest.ApiException(
783
+ status=0,
784
+ reason=(
785
+ "Failed to parse `{0}` as `{1}`"
786
+ .format(data, klass)
787
+ )
788
+ )
789
+
637
790
  def __deserialize_model(self, data, klass):
638
791
  """Deserializes list or dict to model.
639
792
 
@@ -641,27 +794,5 @@ class ApiClient(object):
641
794
  :param klass: class literal.
642
795
  :return: model object.
643
796
  """
644
- has_discriminator = False
645
- if (hasattr(klass, 'get_real_child_model')
646
- and klass.discriminator_value_class_map):
647
- has_discriminator = True
648
-
649
- if not klass.openapi_types and has_discriminator is False:
650
- return data
651
797
 
652
- kwargs = {}
653
- if (data is not None and
654
- klass.openapi_types is not None and
655
- isinstance(data, (list, dict))):
656
- for attr, attr_type in six.iteritems(klass.openapi_types):
657
- if klass.attribute_map[attr] in data:
658
- value = data[klass.attribute_map[attr]]
659
- kwargs[attr] = self.__deserialize(value, attr_type)
660
-
661
- instance = klass(**kwargs)
662
-
663
- if has_discriminator:
664
- klass_name = instance.get_real_child_model(data)
665
- if klass_name:
666
- instance = self.__deserialize(data, klass_name)
667
- return instance
798
+ return klass.from_dict(data)