From bba308c221e8560b9e296a043f0d10aa8ea4362e Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 11:35:17 +0800 Subject: [PATCH 1/9] Improves wait_exponential_jitter --- ...t-exponential-jitter-e0a3a27fef39f97f.yaml | 8 ++++ tenacity/wait.py | 39 +++++++++++-------- tests/test_tenacity.py | 10 ++++- 3 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml diff --git a/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml b/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml new file mode 100644 index 0000000..49ef3f7 --- /dev/null +++ b/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Improves wait_exponential_jitter to inherit wait_exponential + - Reuses `wait_exponential.__call__` method instead of reimplementing from scratch + - Follows argument names of wait_exponential + - Adds `min`` argument + - Supports supplying `max`, `jitter`, `min` arguments as timedelta diff --git a/tenacity/wait.py b/tenacity/wait.py index e1e2fe4..928a105 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -17,6 +17,7 @@ import abc import random import typing +import warnings from tenacity import _utils @@ -194,7 +195,7 @@ def __call__(self, retry_state: "RetryCallState") -> float: return random.uniform(0, high) -class wait_exponential_jitter(wait_base): +class wait_exponential_jitter(wait_exponential): """Wait strategy that applies exponential backoff and jitter. It allows for a customized initial wait, maximum wait and jitter. @@ -208,21 +209,27 @@ class wait_exponential_jitter(wait_base): def __init__( self, - initial: float = 1, - max: float = _utils.MAX_WAIT, # noqa - exp_base: float = 2, - jitter: float = 1, + multiplier: typing.Union[int, float] = 1, + max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa + exp_base: typing.Union[int, float] = 2, + jitter: _utils.time_unit_type = 1, + min: _utils.time_unit_type = 0, # noqa + initial: typing.Optional[float] = None, ) -> None: - self.initial = initial - self.max = max - self.exp_base = exp_base - self.jitter = jitter + if initial is not None and multiplier != 1: + msg = ( + "Received both `multiplier` and `initial` arguments. " + "`initial` is deprecated, use `multiplier` instead." + ) + raise ValueError(msg) + elif initial is not None: + msg = "`initial` is deprecated, use `multiplier` instead." + warnings.warn(msg, DeprecationWarning) + multiplier = jitter + + super().__init__(multiplier, max, exp_base, min) + self.jitter = _utils.to_seconds(jitter) def __call__(self, retry_state: "RetryCallState") -> float: - jitter = random.uniform(0, self.jitter) - try: - exp = self.exp_base ** (retry_state.attempt_number - 1) - result = self.initial * exp + jitter - except OverflowError: - result = self.max - return max(0, min(result, self.max)) + result = super().__call__(retry_state) + random.uniform(0, self.jitter) + return max(self.min, min(result, self.max)) diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index 0e54bc1..656dc85 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -460,9 +460,15 @@ def test_wait_exponential_jitter(self): self.assertEqual(fn(make_retry_state(8, 0)), 60) self.assertEqual(fn(make_retry_state(9, 0)), 60) - fn = tenacity.wait_exponential_jitter(10, 5) + max = 5 + fn = tenacity.wait_exponential_jitter(10, max) for _ in range(1000): - self.assertEqual(fn(make_retry_state(1, 0)), 5) + self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0, max) + + min = 1 + fn = tenacity.wait_exponential_jitter(0.5, min=min) + for _ in range(1000): + self._assert_inclusive_range(fn(make_retry_state(1, 0)), min, 2) # Default arguments exist fn = tenacity.wait_exponential_jitter() From 2fba71321b6d016048ed162ee7c373e504aec420 Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 11:37:34 +0800 Subject: [PATCH 2/9] Update improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml --- .../improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml b/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml index 49ef3f7..d118cf5 100644 --- a/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml +++ b/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml @@ -4,5 +4,5 @@ features: Improves wait_exponential_jitter to inherit wait_exponential - Reuses `wait_exponential.__call__` method instead of reimplementing from scratch - Follows argument names of wait_exponential - - Adds `min`` argument - - Supports supplying `max`, `jitter`, `min` arguments as timedelta + - Adds `min` argument + - Supports supplying `max`, `jitter`, `min` arguments as `timedelta` From cf532a25e639d28a81f0f3de938a80567a24519f Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 11:38:06 +0800 Subject: [PATCH 3/9] Update improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml --- .../notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml b/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml index d118cf5..75736d1 100644 --- a/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml +++ b/releasenotes/notes/improve-wait-exponential-jitter-e0a3a27fef39f97f.yaml @@ -2,7 +2,7 @@ features: - | Improves wait_exponential_jitter to inherit wait_exponential - - Reuses `wait_exponential.__call__` method instead of reimplementing from scratch + - Reuses `wait_exponential.__call__` method instead of duplicating code - Follows argument names of wait_exponential - Adds `min` argument - Supports supplying `max`, `jitter`, `min` arguments as `timedelta` From f3315cfa1509ba9e895e64ca2429ebc1b7f62f5b Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 11:42:49 +0800 Subject: [PATCH 4/9] Improves docstring --- tenacity/wait.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tenacity/wait.py b/tenacity/wait.py index 928a105..997c43d 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -198,12 +198,12 @@ def __call__(self, retry_state: "RetryCallState") -> float: class wait_exponential_jitter(wait_exponential): """Wait strategy that applies exponential backoff and jitter. - It allows for a customized initial wait, maximum wait and jitter. + It allows for a customized multiplier, max wait, jitter and min wait. This implements the strategy described here: https://cloud.google.com/storage/docs/retry-strategy - The wait time is min(initial * 2**n + random.uniform(0, jitter), maximum) + The wait time is max(min, min(multiplier * 2**n + random.uniform(0, jitter), max)) where n is the retry count. """ From 26dee305821f02b150dfd80d8cc07f58905924e3 Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 11:43:51 +0800 Subject: [PATCH 5/9] Update wait.py --- tenacity/wait.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tenacity/wait.py b/tenacity/wait.py index 997c43d..1c41782 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -214,7 +214,7 @@ def __init__( exp_base: typing.Union[int, float] = 2, jitter: _utils.time_unit_type = 1, min: _utils.time_unit_type = 0, # noqa - initial: typing.Optional[float] = None, + initial: typing.Union[int, float] = None, ) -> None: if initial is not None and multiplier != 1: msg = ( From 7e7c215f60e3dd741475e1b3d43056e98022b46e Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 11:45:01 +0800 Subject: [PATCH 6/9] Update wait.py --- tenacity/wait.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tenacity/wait.py b/tenacity/wait.py index 1c41782..22f0177 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -214,7 +214,7 @@ def __init__( exp_base: typing.Union[int, float] = 2, jitter: _utils.time_unit_type = 1, min: _utils.time_unit_type = 0, # noqa - initial: typing.Union[int, float] = None, + initial: typing.Union[int, float, None] = None, ) -> None: if initial is not None and multiplier != 1: msg = ( From a726dfb4ba206d12b69fc4f13efb8e63f086a9e8 Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 15:02:29 +0800 Subject: [PATCH 7/9] Update test_tenacity.py --- tests/test_tenacity.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index 656dc85..545b9d9 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -465,10 +465,10 @@ def test_wait_exponential_jitter(self): for _ in range(1000): self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0, max) - min = 1 - fn = tenacity.wait_exponential_jitter(0.5, min=min) + min = 5 + fn = tenacity.wait_exponential_jitter(min=min) for _ in range(1000): - self._assert_inclusive_range(fn(make_retry_state(1, 0)), min, 2) + self._assert_inclusive_range(fn(make_retry_state(1, 0)), min, 6) # Default arguments exist fn = tenacity.wait_exponential_jitter() From 7d7d538d381d5194a050505f30af9da239af5eda Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Mon, 18 Dec 2023 16:39:42 +0800 Subject: [PATCH 8/9] Renames min, max --- tests/test_tenacity.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index 545b9d9..48f1455 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -460,15 +460,15 @@ def test_wait_exponential_jitter(self): self.assertEqual(fn(make_retry_state(8, 0)), 60) self.assertEqual(fn(make_retry_state(9, 0)), 60) - max = 5 - fn = tenacity.wait_exponential_jitter(10, max) + max_wait = 5 + fn = tenacity.wait_exponential_jitter(10, max_wait) for _ in range(1000): - self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0, max) + self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0, max_wait) - min = 5 - fn = tenacity.wait_exponential_jitter(min=min) + min_wait = 5 + fn = tenacity.wait_exponential_jitter(min=min_wait) for _ in range(1000): - self._assert_inclusive_range(fn(make_retry_state(1, 0)), min, 6) + self._assert_inclusive_range(fn(make_retry_state(1, 0)), min_wait, 6) # Default arguments exist fn = tenacity.wait_exponential_jitter() From 0669bbba86387c68a05d7501af393ac0ae7dfedb Mon Sep 17 00:00:00 2001 From: YuXuan Tay Date: Tue, 19 Dec 2023 08:25:53 +0800 Subject: [PATCH 9/9] Fixes bug of assigning `jitter` to `multiplier` --- tenacity/wait.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tenacity/wait.py b/tenacity/wait.py index 22f0177..30ab913 100644 --- a/tenacity/wait.py +++ b/tenacity/wait.py @@ -225,7 +225,7 @@ def __init__( elif initial is not None: msg = "`initial` is deprecated, use `multiplier` instead." warnings.warn(msg, DeprecationWarning) - multiplier = jitter + multiplier = initial super().__init__(multiplier, max, exp_base, min) self.jitter = _utils.to_seconds(jitter)