syneto_api 0.2.44__tar.gz → 0.2.46__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: syneto-api
3
- Version: 0.2.44
3
+ Version: 0.2.46
4
4
  Summary: Syneto Client API library
5
5
  Home-page: https://github.com/SynetoNet/syneto-api
6
6
  License: Proprietary
@@ -11,13 +11,13 @@ Classifier: License :: Other/Proprietary License
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.7
13
13
  Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
14
  Requires-Dist: aiodns (>=3,<4)
16
15
  Requires-Dist: aiohttp (>=3.7,<4.0)
17
16
  Requires-Dist: cchardet (>=2.1,<3.0)
18
17
  Requires-Dist: inflection (>=0.5,<0.6)
19
18
  Requires-Dist: python-dotenv (>=0.16,<0.17)
20
19
  Requires-Dist: requests[secure] (>=2.25,<3.0)
20
+ Requires-Dist: tenacity (>=8.0.1,<9.0.0)
21
21
  Project-URL: Repository, https://github.com/SynetoNet/syneto-api
22
22
  Description-Content-Type: text/markdown
23
23
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "syneto_api"
3
- version = "0.2.44"
3
+ version = "0.2.46"
4
4
  description = "Syneto Client API library"
5
5
  authors = ["Your Name <you@example.com>"]
6
6
  license = "Proprietary"
@@ -16,6 +16,7 @@ inflection = "^0.5"
16
16
  aiohttp = "^3.7"
17
17
  aiodns = "^3"
18
18
  cchardet = "^2.1"
19
+ tenacity = "^8.0.1"
19
20
 
20
21
  [tool.poetry.dev-dependencies]
21
22
  pytest = "^6.2.2"
@@ -16,11 +16,12 @@ install_requires = \
16
16
  'cchardet>=2.1,<3.0',
17
17
  'inflection>=0.5,<0.6',
18
18
  'python-dotenv>=0.16,<0.17',
19
- 'requests[secure]>=2.25,<3.0']
19
+ 'requests[secure]>=2.25,<3.0',
20
+ 'tenacity>=8.0.1,<9.0.0']
20
21
 
21
22
  setup_kwargs = {
22
23
  'name': 'syneto-api',
23
- 'version': '0.2.44',
24
+ 'version': '0.2.46',
24
25
  'description': 'Syneto Client API library',
25
26
  'long_description': '# Syneto API\n\nSyneto Client API library: authentication, storage, virtualization and protection\n\n# Installation\n\n```\n$ pip install syneto-api\n```\n\n# Basic Usage\n\n```\nfrom syneto_api import Authentication, Virtualization, Storage, Protection\n\nauth_api = Authentication(url_base="https://syneto-instance-ip-address/api/auth", insecure_ssl=True)\nresponse = auth_api.login(username="admin", password="admin")\njwt = response[\'jwt\']\n\nvirt_api = Virtualization(url_base="https://syneto-instance-ip-address/api/virtualization", insecure_ssl=True)\nvirt_api.set_auth_jwt(jwt)\nprint(virt_api.get_vms())\n\nstorage_api = Storage(url_base="https://syneto-instance-ip-address/api/storage", insecure_ssl=True)\nstorage_api.set_auth_jwt(jwt)\nprint(storage_api.get_pools())\n```\n\n# Environment Variables\n\nFor conveninence, the base urls for the api endpoints are also accepted as environment variables, please see below.\n\n```\nAUTH_SERVICE=https://syneto-instance-ip-address/api/auth\nVIRTUALIZATION_SERVICE=https://syneto-instance-ip-address/api/virtualization\nSTORAGE_SERVICE=https://syneto-instance-ip-address/api/storage\nPROTECTION_SERVICE=https://syneto-instance-ip-address/api/protection\n```\n\nIf you are using self-signed SSL certificates, set the following env. so that the http request library does not perform ssl verification. \n\n```\nALLOW_INSECURE_SSL=True\n```\n',
26
27
  'author': 'Your Name',
@@ -6,6 +6,8 @@ import urllib.parse
6
6
  import logging
7
7
  import traceback
8
8
  import urllib.parse
9
+ import concurrent.futures as futures
10
+ from tenacity import Retrying, AsyncRetrying, RetryError, stop_after_delay, retry_if_not_exception_type
9
11
 
10
12
  from .settings import get_env_flag
11
13
  from .keyring import KeyRing
@@ -102,17 +104,27 @@ class APIClientBase:
102
104
  ):
103
105
  try:
104
106
  logger.debug(f"Calling {method} {url} with aiohttp lib")
105
- # loop = asyncio.get_event_loop()
106
- # return loop.run_until_complete(self._async_request(method, url, body, success_codes, error_subject))
107
- return await asyncio.wait_for(
108
- self._unsafe_async_request(method, url, body, success_codes, error_subject, on_response_callback),
109
- timeout=self.timeout,
110
- )
107
+ async for attempt in AsyncRetrying(
108
+ stop=stop_after_delay(self.timeout), retry=retry_if_not_exception_type(APIException), reraise=True
109
+ ):
110
+ with attempt:
111
+ return await asyncio.wait_for(
112
+ self._unsafe_async_request(
113
+ method, url, body, success_codes, error_subject, on_response_callback
114
+ ),
115
+ timeout=self.timeout,
116
+ )
111
117
 
112
118
  except APIException as e:
113
119
  raise
114
120
 
115
- except (aiohttp.ServerTimeoutError, requests.Timeout, asyncio.TimeoutError) as e:
121
+ except (
122
+ aiohttp.ServerTimeoutError,
123
+ requests.Timeout,
124
+ asyncio.TimeoutError,
125
+ futures.TimeoutError,
126
+ RetryError,
127
+ ) as e:
116
128
  logger.warning(f"Failed {method} request to {url}, network timeout: {str(e)}")
117
129
  error = self._append_url_if_not_local_service("network timeout")
118
130
  raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
@@ -184,12 +196,18 @@ class APIClientBase:
184
196
  ):
185
197
  try:
186
198
  logger.debug(f"Calling {method} {url} with requests lib")
187
- return self._unsafe_sync_request(method, url, body, success_codes, error_subject, on_response_callback)
199
+ for attempt in Retrying(
200
+ stop=stop_after_delay(self.timeout), retry=retry_if_not_exception_type(APIException), reraise=True
201
+ ):
202
+ with attempt:
203
+ return self._unsafe_sync_request(
204
+ method, url, body, success_codes, error_subject, on_response_callback
205
+ )
188
206
 
189
207
  except APIException as e:
190
208
  raise
191
209
 
192
- except (aiohttp.ServerTimeoutError, requests.Timeout) as e:
210
+ except (aiohttp.ServerTimeoutError, requests.Timeout, futures.TimeoutError, RetryError) as e:
193
211
  logger.warning(f"Failed {method} request to {url}, network timeout: {str(e)}")
194
212
  error = self._append_url_if_not_local_service("network timeout")
195
213
  raise APIException(url=url, message=f"{error_subject} has failed ({error})", request_body=body) from e
@@ -225,7 +243,7 @@ class APIClientBase:
225
243
  if body is not None:
226
244
  method_args["json"] = body
227
245
 
228
- response = method_func(url, timeout=60, **method_args)
246
+ response = method_func(url, timeout=self.timeout, **method_args)
229
247
  is_json = "json" in response.headers.get("content-type", "").lower()
230
248
  if response.status_code in success_codes:
231
249
  logger.debug(f"Received {response.status_code} from {method} {url}")
@@ -255,16 +273,19 @@ class APIClientBase:
255
273
  def _append_url_if_not_local_service(self, message):
256
274
  if self._is_local_service():
257
275
  return message
276
+
277
+ hostname = self._get_hostname()
278
+ if not hostname:
279
+ return f"{message}: <host not specified>"
258
280
  else:
259
- hostname = self._get_hostname()
260
- return f"{message}: {hostname}"
281
+ return f"{message}: {hostname.strip()}"
261
282
 
262
283
  def _get_hostname(self):
263
284
  parsed_uri = urllib.parse.urlparse(self.url_base)
264
285
  hostname = parsed_uri.netloc
265
286
  if ":" in hostname:
266
287
  hostname, port = parsed_uri.netloc.split(":")[:2]
267
- return hostname
288
+ return hostname.strip() if hostname else hostname
268
289
 
269
290
  def _get_method_func(self, method):
270
291
  requests_methods = {
File without changes