unique_sdk 0.9.11__py3-none-any.whl → 0.9.13__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.
@@ -339,13 +339,14 @@ class APIRequestor(object):
339
339
  rcode,
340
340
  rheaders,
341
341
  )
342
- except Exception:
342
+ except Exception as e:
343
343
  raise _error.APIError(
344
344
  "Invalid response body from API: %s "
345
345
  "(HTTP response code was %d)" % (rbody, rcode),
346
346
  cast(bytes, rbody),
347
347
  rcode,
348
348
  rheaders,
349
+ original_error=e,
349
350
  )
350
351
  if self._should_handle_code_as_error(rcode):
351
352
  self.handle_error_response(rbody, rcode, resp.data, rheaders)
@@ -374,33 +375,57 @@ class APIRequestor(object):
374
375
  raise err
375
376
 
376
377
  def specific_api_error(self, rbody, rcode, resp, rheaders, error_data):
378
+ cause = error_data.get("cause", {})
379
+ status = cause.get("status", rcode)
380
+
377
381
  _util.log_info(
378
382
  "Unique error received",
379
- error_code=error_data.get("code"),
383
+ error_code=status,
380
384
  error_type=error_data.get("type"),
381
385
  error_message=error_data.get("message"),
382
386
  error_params=error_data.get("params"),
383
387
  )
384
388
 
385
- if rcode in [400, 404]:
389
+ error = cause.get("error", {}) if cause else {}
390
+ error_code = error.get("code", "<Unknown code>")
391
+ error_message = error.get("message", "<No message>")
392
+ original_error = f"{error_code}: {error_message}"
393
+
394
+ if status in [400, 404]:
386
395
  return _error.InvalidRequestError(
387
- error_data.get("message"),
388
- error_data.get("params"),
389
- error_data.get("code"),
390
- rbody,
391
- rcode,
392
- resp,
393
- rheaders,
396
+ message=error_data.get("message"),
397
+ params=error_data.get("params"),
398
+ code=error_data.get("code"),
399
+ http_body=rbody,
400
+ http_status=rcode,
401
+ json_body=resp,
402
+ headers=rheaders,
403
+ original_error=original_error,
394
404
  )
395
- elif rcode == 401:
405
+ elif status == 401:
396
406
  return _error.AuthenticationError(
397
- error_data.get("message"), rbody, rcode, resp, rheaders
407
+ message=error_data.get("message"),
408
+ http_body=rbody,
409
+ http_status=status,
410
+ json_body=resp,
411
+ headers=rheaders,
412
+ original_error=original_error,
398
413
  )
399
- elif rcode == 403:
414
+ elif status == 403:
400
415
  return _error.PermissionError(
401
- error_data.get("message"), rbody, rcode, resp, rheaders
416
+ message=error_data.get("message"),
417
+ http_body=rbody,
418
+ http_status=status,
419
+ json_body=resp,
420
+ headers=rheaders,
421
+ original_error=original_error,
402
422
  )
403
423
  else:
404
424
  return _error.APIError(
405
- error_data.get("message"), rbody, rcode, resp, rheaders
425
+ message=error_data.get("message"),
426
+ http_body=rbody,
427
+ http_status=status,
428
+ json_body=resp,
429
+ headers=rheaders,
430
+ original_error=original_error,
406
431
  )
@@ -22,10 +22,12 @@ retry_dict = {
22
22
  "error_messages": [
23
23
  "problem proxying the request",
24
24
  "Upstream service reached a hard timeout",
25
+ "Invalid response body from API",
25
26
  ],
26
27
  "max_retries": 3,
27
28
  "backoff_factor": 2,
28
29
  "initial_delay": 1,
30
+ "should_retry_5xx": True,
29
31
  }
30
32
 
31
33
 
unique_sdk/_error.py CHANGED
@@ -9,6 +9,7 @@ class UniqueError(Exception):
9
9
  headers: Optional[Dict[str, str]]
10
10
  code: Optional[str]
11
11
  request_id: Optional[str]
12
+ original_error: Optional[Exception | str]
12
13
 
13
14
  def __init__(
14
15
  self,
@@ -18,6 +19,7 @@ class UniqueError(Exception):
18
19
  json_body: Optional[object] = None,
19
20
  headers: Optional[Dict[str, str]] = None,
20
21
  code: Optional[str] = None,
22
+ original_error: Optional[Exception | str] = None,
21
23
  ):
22
24
  super(UniqueError, self).__init__(message)
23
25
 
@@ -38,6 +40,13 @@ class UniqueError(Exception):
38
40
  self.headers = headers or {}
39
41
  self.code = code
40
42
  self.request_id = self.headers.get("request-id", None)
43
+ self.original_error = original_error
44
+
45
+ def __str__(self):
46
+ msg = self._message or "<empty message>"
47
+ if self.original_error:
48
+ msg += f"\n(Original error) {str(self.original_error)}"
49
+ return msg
41
50
 
42
51
 
43
52
  class UniqueErrorWithParamsCode(UniqueError):
@@ -72,9 +81,10 @@ class APIConnectionError(UniqueError):
72
81
  headers=None,
73
82
  code=None,
74
83
  should_retry=False,
84
+ original_error=None,
75
85
  ):
76
86
  super(APIConnectionError, self).__init__(
77
- message, http_body, http_status, json_body, headers, code
87
+ message, http_body, http_status, json_body, headers, code, original_error
78
88
  )
79
89
  self.should_retry = should_retry
80
90
 
@@ -97,9 +107,10 @@ class InvalidRequestError(UniqueErrorWithParamsCode):
97
107
  http_status=None,
98
108
  json_body=None,
99
109
  headers=None,
110
+ original_error=None,
100
111
  ):
101
112
  super(InvalidRequestError, self).__init__(
102
- message, http_body, http_status, json_body, headers, code
113
+ message, http_body, http_status, json_body, headers, code, original_error
103
114
  )
104
115
  self.params = params
105
116
 
@@ -130,11 +130,12 @@ class RequestsClient(HTTPClient):
130
130
  content = result.content
131
131
  status_code = result.status_code
132
132
 
133
- except Exception:
133
+ except Exception as e:
134
134
  raise _error.APIConnectionError(
135
135
  "Unexpected error communicating with Unique. "
136
136
  "If this problem persists, let us know at support@unique.ch",
137
137
  http_status=500,
138
+ original_error=e,
138
139
  )
139
140
 
140
141
  return content, status_code, result.headers
@@ -186,11 +187,12 @@ class HTTPXClient(HTTPClient):
186
187
  args, kwargs = self._get_request_args_kwargs(method, url, headers, post_data)
187
188
  try:
188
189
  response = self._client.request(*args, **kwargs)
189
- except Exception:
190
+ except Exception as e:
190
191
  raise _error.APIConnectionError(
191
192
  "Unexpected error communicating with Unique. "
192
193
  "If this problem persists, let us know at support@unique.ch",
193
194
  http_status=500,
195
+ original_error=e,
194
196
  )
195
197
 
196
198
  content = response.content
@@ -208,11 +210,12 @@ class HTTPXClient(HTTPClient):
208
210
  args, kwargs = self._get_request_args_kwargs(method, url, headers, post_data)
209
211
  try:
210
212
  response = await self._client_async.request(*args, **kwargs)
211
- except Exception:
213
+ except Exception as e:
212
214
  raise _error.APIConnectionError(
213
215
  "Unexpected error communicating with Unique. "
214
216
  "If this problem persists, let us know at support@unique.ch",
215
217
  http_status=500,
218
+ original_error=e,
216
219
  )
217
220
 
218
221
  content = response.content
@@ -297,11 +300,12 @@ class AIOHTTPClient(HTTPClient):
297
300
  args, kwargs = self._get_request_args_kwargs(method, url, headers, post_data)
298
301
  try:
299
302
  response = await self._session.request(*args, **kwargs)
300
- except Exception:
303
+ except Exception as e:
301
304
  raise _error.APIConnectionError(
302
305
  "Unexpected error communicating with Unique. "
303
306
  "If this problem persists, let us know at support@unique.ch",
304
307
  http_status=500,
308
+ original_error=e,
305
309
  )
306
310
 
307
311
  content = response.content
unique_sdk/_util.py CHANGED
@@ -200,6 +200,7 @@ def retry_on_error(
200
200
  initial_delay: int = 1,
201
201
  backoff_factor: int = 2,
202
202
  error_class=APIError,
203
+ should_retry_5xx=False,
203
204
  ):
204
205
  def decorator(func: Callable) -> Callable:
205
206
  @wraps(func)
@@ -209,10 +210,15 @@ def retry_on_error(
209
210
  try:
210
211
  return await func(*args, **kwargs)
211
212
  except Exception as e:
212
- if not any(
213
+ should_retry = any(
213
214
  err_msg.lower() in str(e).lower() for err_msg in error_messages
214
- ):
215
- raise e # Raise the error if none of the messages match
215
+ )
216
+ # Add 5xx check if `should_retry_5xx` is True
217
+ if should_retry_5xx and hasattr(e, "status_code"):
218
+ should_retry = should_retry or (500 <= e.status_code < 600)
219
+
220
+ if not should_retry:
221
+ raise e # Raise the error if no retry condition is met
216
222
 
217
223
  attempts += 1
218
224
  if attempts >= max_retries:
@@ -229,10 +235,15 @@ def retry_on_error(
229
235
  try:
230
236
  return func(*args, **kwargs)
231
237
  except Exception as e:
232
- if not any(
238
+ should_retry = any(
233
239
  err_msg.lower() in str(e).lower() for err_msg in error_messages
234
- ):
235
- raise e # Raise the error if none of the messages match
240
+ )
241
+ # Add 5xx check if `should_retry_5xx` is True
242
+ if should_retry_5xx and hasattr(e, "status_code"):
243
+ should_retry = should_retry or (500 <= e.status_code < 600)
244
+
245
+ if not should_retry:
246
+ raise e # Raise the error if no retry condition is met
236
247
 
237
248
  attempts += 1
238
249
  if attempts >= max_retries:
unique_sdk/_version.py CHANGED
@@ -1 +1 @@
1
- VERSION = "0.9.8"
1
+ VERSION = "0.9.13"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_sdk
3
- Version: 0.9.11
3
+ Version: 0.9.13
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: Martin Fadler
@@ -899,6 +899,12 @@ All notable changes to this project will be documented in this file.
899
899
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
900
900
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
901
901
 
902
+ ## [0.9.13] - 2024-10-23
903
+ - Add retry for `5xx` errors, add additional error message.
904
+
905
+ ## [0.9.12] - 2024-11-21
906
+ - Include original error message in returned exceptions
907
+
902
908
  ## [0.9.11] - 2024-11-18
903
909
  - Add `ingestionConfig` to `UpsertParams.Input` parameters
904
910
 
@@ -1,17 +1,17 @@
1
1
  unique_sdk/__init__.py,sha256=Cv42hZ3dlsNACMIza7LDARXiY-w7a_ijlSuY_jADJD8,3072
2
- unique_sdk/_api_requestor.py,sha256=-Be3hCCjTrLsWgb9gW8Uq_wFFDxloOpZpTmp56zu858,13605
3
- unique_sdk/_api_resource.py,sha256=zKu-Sk1sJ6b9mr8ljrt9UBmi6y6lO7A29DNcBxdRYD4,6113
2
+ unique_sdk/_api_requestor.py,sha256=O6iF_d_jc1ceTYVa3kOQHXjYKdmBp0kGW_pfMw1ClwE,14543
3
+ unique_sdk/_api_resource.py,sha256=tE3J9sC5qwOdIMGLYEyvHjdQRYX-1unrLatO7CzgSfk,6185
4
4
  unique_sdk/_api_version.py,sha256=Ku4JPdeyJtnX5eJJvRCEc1_u44UObdVrvrL1T-WwWCs,46
5
- unique_sdk/_error.py,sha256=gQ2jaZYCdZa32zzDPPexc3u7N2j1gyjPc-AlMLWH_lY,2912
6
- unique_sdk/_http_client.py,sha256=BB5QUDHVBlj4sa4cbNu7Dp0t7bjLwheN9T5J9g3L1dk,10213
5
+ unique_sdk/_error.py,sha256=j-deT0PJ-exLCwUkqORRaxaLLrGunDag9bKJSmBBKZI,3343
6
+ unique_sdk/_http_client.py,sha256=CSc2gduhQ42v6G1oh4Jyg8XL7Sa3YQ7WdTpc6kAyTBI,10369
7
7
  unique_sdk/_list_object.py,sha256=k3fqWhD-37XHh6gZMHnuqOc1Bomq265Lmy2T7Rapxrk,3949
8
8
  unique_sdk/_object_classes.py,sha256=udAQucYaJBpgo7BAvga_JXAK92hu4Ohl2mOq1aN9J_A,270
9
9
  unique_sdk/_request_options.py,sha256=oHh2AKka6j9pO53Htur3Wj0VJSusEjq8zkXYY179B_E,255
10
10
  unique_sdk/_unique_object.py,sha256=PLaIzb6NPYghfMKHqDx_ZyojcDcuQzTrATXrktLqMa0,11442
11
11
  unique_sdk/_unique_ql.py,sha256=aUYXhYPA-_kPfImTapilCyDvB_53YpLd51kxILe7fDA,1707
12
12
  unique_sdk/_unique_response.py,sha256=q19hIxlsrkLAQylfHA0webp6bsCCHXkDJDwaC69Iev8,576
13
- unique_sdk/_util.py,sha256=ddCV4IDbbf2fh1Z09X7Y2REQmXMETLrM7tKLOc33w0o,8240
14
- unique_sdk/_version.py,sha256=bLj15cDf-jfrKLtrOqhC3kl-ZIlt9q8ieogbrosWe_g,18
13
+ unique_sdk/_util.py,sha256=J8L4ynMM6_zzN5uaIxriShr9lYpYa_5RMQAWKuqIcC0,8806
14
+ unique_sdk/_version.py,sha256=K80rxBhBBS3zPRQP96Dx9umWIad1LHJr2TPbKxFCbgE,19
15
15
  unique_sdk/_webhook.py,sha256=GYxbUibQN_W4XlNTHaMIksT9FQJk4LJmlKcxOu3jqiU,2855
16
16
  unique_sdk/api_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  unique_sdk/api_resources/_acronyms.py,sha256=GIU1XH1flGWQYcpsFqTYwg4ioIGxVmb15tux84nmhEg,891
@@ -28,7 +28,7 @@ unique_sdk/utils/chat_history.py,sha256=5UqL9hF1O9pV7skbNOlEibF5rHdYsmG3m5-YEPUo
28
28
  unique_sdk/utils/file_io.py,sha256=tcS-5NA97AyiJPhKpWs3i0qKNFsZlttToxrvnWRDJrs,3857
29
29
  unique_sdk/utils/sources.py,sha256=wfboE-neMKa0Wuq9QzfAEFMkNLrIrmm0v-QF33sLo6k,4952
30
30
  unique_sdk/utils/token.py,sha256=AzKuAA1AwBtnvSFxGcsHLpxXr_wWE5Mj4jYBbOz2ljA,1740
31
- unique_sdk-0.9.11.dist-info/LICENSE,sha256=EJCWoHgrXVBUb47PnjeV4MFIEOR71MAdCOIgv61J-4k,1065
32
- unique_sdk-0.9.11.dist-info/METADATA,sha256=RjHhIAWryMqKEzBEqHDGI4SKMGWZDF1s6t_7jvMWOrU,27975
33
- unique_sdk-0.9.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
34
- unique_sdk-0.9.11.dist-info/RECORD,,
31
+ unique_sdk-0.9.13.dist-info/LICENSE,sha256=EJCWoHgrXVBUb47PnjeV4MFIEOR71MAdCOIgv61J-4k,1065
32
+ unique_sdk-0.9.13.dist-info/METADATA,sha256=FKP4b3n6sn6qVS3DUOoTIueOHBol5SodfydJ6hMcUrc,28143
33
+ unique_sdk-0.9.13.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
34
+ unique_sdk-0.9.13.dist-info/RECORD,,