From d58ae5f6e069935ebdc56187d455f64278a8323b Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Tue, 7 Nov 2023 09:22:18 +0100 Subject: [PATCH 01/10] Dumbing down our use of tenacity. --- src/sumo/wrapper/_blob_client.py | 20 +++++++-- src/sumo/wrapper/sumo_client.py | 69 +++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index 8dd3273..ca2ac69 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -1,13 +1,19 @@ import httpx -from ._decorators import raise_for_status, http_retry, raise_for_status_async - +from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async +from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential_jitter, stop_after_attempt class BlobClient: """Upload blobs to blob store using pre-authorized URLs""" @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -26,7 +32,13 @@ def upload_blob(self, blob: bytes, url: str): return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index 19c271b..a22850c 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -9,7 +9,10 @@ from ._auth_provider import get_auth_provider from .config import APP_REGISTRATION, TENANT_ID, AUTHORITY_HOST_URI -from ._decorators import raise_for_status, http_retry, raise_for_status_async +from ._decorators import is_retryable_exception, \ + is_retryable_status_code, raise_for_status, raise_for_status_async +from tenacity import retry, retry_if_exception, retry_if_result, \ + wait_exponential_jitter, stop_after_attempt logger = logging.getLogger("sumo.wrapper") @@ -109,7 +112,13 @@ def blob_client(self) -> BlobClient: return self._blob_client @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -156,7 +165,13 @@ def get(self, path: str, params: dict = None) -> dict: return response @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def post( self, path: str, @@ -229,7 +244,13 @@ def post( return response @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def put( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -274,7 +295,13 @@ def put( return response @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -329,7 +356,13 @@ def getLogger(self, name): return logger @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -375,7 +408,13 @@ async def get_async(self, path: str, params: dict = None): return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def post_async( self, path: str, @@ -451,7 +490,13 @@ async def post_async( return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def put_async( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -497,7 +542,13 @@ async def put_async( return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From a5f16113bf38cd54c3b9a62fbd2197fcd43001ba Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Tue, 7 Nov 2023 10:41:14 +0100 Subject: [PATCH 02/10] Add reraise=True to uses of tenacity.retry. --- src/sumo/wrapper/_blob_client.py | 2 ++ src/sumo/wrapper/sumo_client.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index ca2ac69..e1134e9 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -13,6 +13,7 @@ class BlobClient: | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -38,6 +39,7 @@ def upload_blob(self, blob: bytes, url: str): | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index a22850c..f0d3140 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -118,6 +118,7 @@ def blob_client(self) -> BlobClient: | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -171,6 +172,7 @@ def get(self, path: str, params: dict = None) -> dict: | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def post( self, @@ -250,6 +252,7 @@ def post( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def put( self, path: str, blob: bytes = None, json: dict = None @@ -301,6 +304,7 @@ def put( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -362,6 +366,7 @@ def getLogger(self, name): | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -414,6 +419,7 @@ async def get_async(self, path: str, params: dict = None): | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def post_async( self, @@ -496,6 +502,7 @@ async def post_async( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def put_async( self, path: str, blob: bytes = None, json: dict = None @@ -548,6 +555,7 @@ async def put_async( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From 6be74bc04e9f728ab1af4480099927ec598d9991 Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Tue, 7 Nov 2023 12:02:18 +0100 Subject: [PATCH 03/10] Change backoff strategy for tenacity, and return value from last attempt even if it failed. --- src/sumo/wrapper/_blob_client.py | 10 ++++++---- src/sumo/wrapper/_decorators.py | 4 ++++ src/sumo/wrapper/sumo_client.py | 28 ++++++++++++++++++---------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index e1134e9..45c4128 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -1,7 +1,7 @@ import httpx -from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async -from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential_jitter, stop_after_attempt +from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value +from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential, wait_random_exponential, stop_after_attempt class BlobClient: """Upload blobs to blob store using pre-authorized URLs""" @@ -12,8 +12,9 @@ class BlobClient: retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -38,8 +39,9 @@ def upload_blob(self, blob: bytes, url: str): retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/_decorators.py b/src/sumo/wrapper/_decorators.py index a2c52cc..2c11451 100644 --- a/src/sumo/wrapper/_decorators.py +++ b/src/sumo/wrapper/_decorators.py @@ -50,6 +50,10 @@ def is_retryable_status_code(response): return response.status_code in [502, 503, 504] +def return_last_value(retry_state): + return retry_state.outcome.result() + + def http_retry(func): return tn.retry( func, diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index f0d3140..ae374c9 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -10,9 +10,9 @@ from .config import APP_REGISTRATION, TENANT_ID, AUTHORITY_HOST_URI from ._decorators import is_retryable_exception, \ - is_retryable_status_code, raise_for_status, raise_for_status_async + is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value from tenacity import retry, retry_if_exception, retry_if_result, \ - wait_exponential_jitter, stop_after_attempt + wait_exponential, wait_random_exponential, stop_after_attempt logger = logging.getLogger("sumo.wrapper") @@ -117,8 +117,9 @@ def blob_client(self) -> BlobClient: retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -171,8 +172,9 @@ def get(self, path: str, params: dict = None) -> dict: retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def post( self, @@ -251,8 +253,9 @@ def post( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def put( self, path: str, blob: bytes = None, json: dict = None @@ -303,8 +306,9 @@ def put( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -365,8 +369,9 @@ def getLogger(self, name): retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -418,8 +423,9 @@ async def get_async(self, path: str, params: dict = None): retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def post_async( self, @@ -501,8 +507,9 @@ async def post_async( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def put_async( self, path: str, blob: bytes = None, json: dict = None @@ -554,8 +561,9 @@ async def put_async( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From c28f16c1b9dd539f11882f212be58e2ca7a99bdd Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Thu, 9 Nov 2023 09:40:38 +0100 Subject: [PATCH 04/10] Harmonize dependency versions with komodo. --- requirements/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 1baf246..784e97c 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,8 +1,8 @@ -msal>=1.18.0 +msal>=1.24.1 msal-extensions>=1.0.0 -PyYAML>=5.4 -setuptools>=49.2.1 +PyYAML>=6.0.1 +setuptools>=68.0.0 pyjwt>=2.4.0 -httpx>=0.25.0 -tenacity>=8.2.3 -azure-identity>=1.14.0 +httpx>=0.24.1 +tenacity>=8.2.2 +azure-identity>=1.13.0 From 5b82c31b27a4f436c3f34b46822dbb6ec4adde8c Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Thu, 9 Nov 2023 10:18:51 +0100 Subject: [PATCH 05/10] Trying to keep 'black' happy. --- src/sumo/wrapper/_blob_client.py | 58 ++++++---- src/sumo/wrapper/sumo_client.py | 179 ++++++++++++++++++------------- 2 files changed, 141 insertions(+), 96 deletions(-) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index 45c4128..672b612 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -1,21 +1,37 @@ import httpx -from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value -from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential, wait_random_exponential, stop_after_attempt +from ._decorators import ( + is_retryable_exception, + is_retryable_status_code, + raise_for_status, + raise_for_status_async, + return_last_value, +) +from tenacity import ( + retry, + retry_if_exception, + retry_if_result, + wait_exponential, + wait_random_exponential, + stop_after_attempt, +) + class BlobClient: """Upload blobs to blob store using pre-authorized URLs""" @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -34,15 +50,17 @@ def upload_blob(self, blob: bytes, url: str): return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index ae374c9..4640abd 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -9,10 +9,21 @@ from ._auth_provider import get_auth_provider from .config import APP_REGISTRATION, TENANT_ID, AUTHORITY_HOST_URI -from ._decorators import is_retryable_exception, \ - is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value -from tenacity import retry, retry_if_exception, retry_if_result, \ - wait_exponential, wait_random_exponential, stop_after_attempt +from ._decorators import ( + is_retryable_exception, + is_retryable_status_code, + raise_for_status, + raise_for_status_async, + return_last_value, +) +from tenacity import ( + retry, + retry_if_exception, + retry_if_result, + wait_exponential, + wait_random_exponential, + stop_after_attempt, +) logger = logging.getLogger("sumo.wrapper") @@ -112,15 +123,17 @@ def blob_client(self) -> BlobClient: return self._blob_client @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -167,15 +180,17 @@ def get(self, path: str, params: dict = None) -> dict: return response @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def post( self, path: str, @@ -248,15 +263,17 @@ def post( return response @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def put( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -301,15 +318,17 @@ def put( return response @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -364,15 +383,17 @@ def getLogger(self, name): return logger @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -418,15 +439,17 @@ async def get_async(self, path: str, params: dict = None): return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def post_async( self, path: str, @@ -502,15 +525,17 @@ async def post_async( return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def put_async( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -556,15 +581,17 @@ async def put_async( return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From 130ea953a31547ca780a7a7d1fbc4f7341f49d5e Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Tue, 7 Nov 2023 09:22:18 +0100 Subject: [PATCH 06/10] Dumbing down our use of tenacity. --- src/sumo/wrapper/_blob_client.py | 20 +++++++-- src/sumo/wrapper/sumo_client.py | 69 +++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index 8dd3273..ca2ac69 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -1,13 +1,19 @@ import httpx -from ._decorators import raise_for_status, http_retry, raise_for_status_async - +from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async +from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential_jitter, stop_after_attempt class BlobClient: """Upload blobs to blob store using pre-authorized URLs""" @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -26,7 +32,13 @@ def upload_blob(self, blob: bytes, url: str): return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index 19c271b..a22850c 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -9,7 +9,10 @@ from ._auth_provider import get_auth_provider from .config import APP_REGISTRATION, TENANT_ID, AUTHORITY_HOST_URI -from ._decorators import raise_for_status, http_retry, raise_for_status_async +from ._decorators import is_retryable_exception, \ + is_retryable_status_code, raise_for_status, raise_for_status_async +from tenacity import retry, retry_if_exception, retry_if_result, \ + wait_exponential_jitter, stop_after_attempt logger = logging.getLogger("sumo.wrapper") @@ -109,7 +112,13 @@ def blob_client(self) -> BlobClient: return self._blob_client @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -156,7 +165,13 @@ def get(self, path: str, params: dict = None) -> dict: return response @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def post( self, path: str, @@ -229,7 +244,13 @@ def post( return response @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def put( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -274,7 +295,13 @@ def put( return response @raise_for_status - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -329,7 +356,13 @@ def getLogger(self, name): return logger @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -375,7 +408,13 @@ async def get_async(self, path: str, params: dict = None): return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def post_async( self, path: str, @@ -451,7 +490,13 @@ async def post_async( return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def put_async( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -497,7 +542,13 @@ async def put_async( return response @raise_for_status_async - @http_retry + @retry(stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential_jitter(), + ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From c9c01f8fe22523b780018c42b3c48a66923c7d60 Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Tue, 7 Nov 2023 10:41:14 +0100 Subject: [PATCH 07/10] Add reraise=True to uses of tenacity.retry. --- src/sumo/wrapper/_blob_client.py | 2 ++ src/sumo/wrapper/sumo_client.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index ca2ac69..e1134e9 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -13,6 +13,7 @@ class BlobClient: | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -38,6 +39,7 @@ def upload_blob(self, blob: bytes, url: str): | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index a22850c..f0d3140 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -118,6 +118,7 @@ def blob_client(self) -> BlobClient: | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -171,6 +172,7 @@ def get(self, path: str, params: dict = None) -> dict: | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def post( self, @@ -250,6 +252,7 @@ def post( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def put( self, path: str, blob: bytes = None, json: dict = None @@ -301,6 +304,7 @@ def put( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -362,6 +366,7 @@ def getLogger(self, name): | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -414,6 +419,7 @@ async def get_async(self, path: str, params: dict = None): | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def post_async( self, @@ -496,6 +502,7 @@ async def post_async( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def put_async( self, path: str, blob: bytes = None, json: dict = None @@ -548,6 +555,7 @@ async def put_async( | retry_if_result(is_retryable_status_code) ), wait=wait_exponential_jitter(), + reraise=True, ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From c59102252fd219635c00ae0ce7483635f5d37b88 Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Tue, 7 Nov 2023 12:02:18 +0100 Subject: [PATCH 08/10] Change backoff strategy for tenacity, and return value from last attempt even if it failed. --- src/sumo/wrapper/_blob_client.py | 10 ++++++---- src/sumo/wrapper/_decorators.py | 4 ++++ src/sumo/wrapper/sumo_client.py | 28 ++++++++++++++++++---------- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index e1134e9..45c4128 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -1,7 +1,7 @@ import httpx -from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async -from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential_jitter, stop_after_attempt +from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value +from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential, wait_random_exponential, stop_after_attempt class BlobClient: """Upload blobs to blob store using pre-authorized URLs""" @@ -12,8 +12,9 @@ class BlobClient: retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -38,8 +39,9 @@ def upload_blob(self, blob: bytes, url: str): retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/_decorators.py b/src/sumo/wrapper/_decorators.py index a2c52cc..2c11451 100644 --- a/src/sumo/wrapper/_decorators.py +++ b/src/sumo/wrapper/_decorators.py @@ -50,6 +50,10 @@ def is_retryable_status_code(response): return response.status_code in [502, 503, 504] +def return_last_value(retry_state): + return retry_state.outcome.result() + + def http_retry(func): return tn.retry( func, diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index f0d3140..ae374c9 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -10,9 +10,9 @@ from .config import APP_REGISTRATION, TENANT_ID, AUTHORITY_HOST_URI from ._decorators import is_retryable_exception, \ - is_retryable_status_code, raise_for_status, raise_for_status_async + is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value from tenacity import retry, retry_if_exception, retry_if_result, \ - wait_exponential_jitter, stop_after_attempt + wait_exponential, wait_random_exponential, stop_after_attempt logger = logging.getLogger("sumo.wrapper") @@ -117,8 +117,9 @@ def blob_client(self) -> BlobClient: retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -171,8 +172,9 @@ def get(self, path: str, params: dict = None) -> dict: retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def post( self, @@ -251,8 +253,9 @@ def post( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def put( self, path: str, blob: bytes = None, json: dict = None @@ -303,8 +306,9 @@ def put( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -365,8 +369,9 @@ def getLogger(self, name): retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -418,8 +423,9 @@ async def get_async(self, path: str, params: dict = None): retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def post_async( self, @@ -501,8 +507,9 @@ async def post_async( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def put_async( self, path: str, blob: bytes = None, json: dict = None @@ -554,8 +561,9 @@ async def put_async( retry_if_exception(is_retryable_exception) | retry_if_result(is_retryable_status_code) ), - wait=wait_exponential_jitter(), + wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), reraise=True, + retry_error_callback=return_last_value, ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API. From ed6cd9ad8cc67045fb1a39228c9cfaf43768c321 Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Thu, 9 Nov 2023 09:40:38 +0100 Subject: [PATCH 09/10] Harmonize dependency versions with komodo. --- requirements/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 1baf246..784e97c 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,8 +1,8 @@ -msal>=1.18.0 +msal>=1.24.1 msal-extensions>=1.0.0 -PyYAML>=5.4 -setuptools>=49.2.1 +PyYAML>=6.0.1 +setuptools>=68.0.0 pyjwt>=2.4.0 -httpx>=0.25.0 -tenacity>=8.2.3 -azure-identity>=1.14.0 +httpx>=0.24.1 +tenacity>=8.2.2 +azure-identity>=1.13.0 From 3796ac91e2e415dc23aa186a7395218d0c01aba8 Mon Sep 17 00:00:00 2001 From: Raymond Wiker Date: Thu, 9 Nov 2023 10:18:51 +0100 Subject: [PATCH 10/10] Trying to keep 'black' happy. --- src/sumo/wrapper/_blob_client.py | 58 ++++++---- src/sumo/wrapper/sumo_client.py | 179 ++++++++++++++++++------------- 2 files changed, 141 insertions(+), 96 deletions(-) diff --git a/src/sumo/wrapper/_blob_client.py b/src/sumo/wrapper/_blob_client.py index 45c4128..672b612 100644 --- a/src/sumo/wrapper/_blob_client.py +++ b/src/sumo/wrapper/_blob_client.py @@ -1,21 +1,37 @@ import httpx -from ._decorators import is_retryable_exception, is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value -from tenacity import retry, retry_if_exception, retry_if_result, wait_exponential, wait_random_exponential, stop_after_attempt +from ._decorators import ( + is_retryable_exception, + is_retryable_status_code, + raise_for_status, + raise_for_status_async, + return_last_value, +) +from tenacity import ( + retry, + retry_if_exception, + retry_if_result, + wait_exponential, + wait_random_exponential, + stop_after_attempt, +) + class BlobClient: """Upload blobs to blob store using pre-authorized URLs""" @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def upload_blob(self, blob: bytes, url: str): """Upload a blob. @@ -34,15 +50,17 @@ def upload_blob(self, blob: bytes, url: str): return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def upload_blob_async(self, blob: bytes, url: str): """Upload a blob async. diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index ae374c9..4640abd 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -9,10 +9,21 @@ from ._auth_provider import get_auth_provider from .config import APP_REGISTRATION, TENANT_ID, AUTHORITY_HOST_URI -from ._decorators import is_retryable_exception, \ - is_retryable_status_code, raise_for_status, raise_for_status_async, return_last_value -from tenacity import retry, retry_if_exception, retry_if_result, \ - wait_exponential, wait_random_exponential, stop_after_attempt +from ._decorators import ( + is_retryable_exception, + is_retryable_status_code, + raise_for_status, + raise_for_status_async, + return_last_value, +) +from tenacity import ( + retry, + retry_if_exception, + retry_if_result, + wait_exponential, + wait_random_exponential, + stop_after_attempt, +) logger = logging.getLogger("sumo.wrapper") @@ -112,15 +123,17 @@ def blob_client(self) -> BlobClient: return self._blob_client @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def get(self, path: str, params: dict = None) -> dict: """Performs a GET-request to the Sumo API. @@ -167,15 +180,17 @@ def get(self, path: str, params: dict = None) -> dict: return response @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def post( self, path: str, @@ -248,15 +263,17 @@ def post( return response @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def put( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -301,15 +318,17 @@ def put( return response @raise_for_status - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) def delete(self, path: str, params: dict = None) -> dict: """Performs a DELETE-request to the Sumo API. @@ -364,15 +383,17 @@ def getLogger(self, name): return logger @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def get_async(self, path: str, params: dict = None): """Performs an async GET-request to the Sumo API. @@ -418,15 +439,17 @@ async def get_async(self, path: str, params: dict = None): return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def post_async( self, path: str, @@ -502,15 +525,17 @@ async def post_async( return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def put_async( self, path: str, blob: bytes = None, json: dict = None ) -> httpx.Response: @@ -556,15 +581,17 @@ async def put_async( return response @raise_for_status_async - @retry(stop=stop_after_attempt(6), - retry=( - retry_if_exception(is_retryable_exception) - | retry_if_result(is_retryable_status_code) - ), - wait=wait_exponential(multiplier=0.5)+wait_random_exponential(multiplier=0.5), - reraise=True, - retry_error_callback=return_last_value, - ) + @retry( + stop=stop_after_attempt(6), + retry=( + retry_if_exception(is_retryable_exception) + | retry_if_result(is_retryable_status_code) + ), + wait=wait_exponential(multiplier=0.5) + + wait_random_exponential(multiplier=0.5), + reraise=True, + retry_error_callback=return_last_value, + ) async def delete_async(self, path: str, params: dict = None) -> dict: """Performs an async DELETE-request to the Sumo API.