From 333c1b56370004d9fce4b634e9d2973b6920e6d7 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Wed, 11 Mar 2026 14:38:59 +0100 Subject: [PATCH] feat: deprecate `initial` in favor of `multiplier` in `wait_exponential_jitter` Every other exponential wait class uses `multiplier`. Having `initial` only on `wait_exponential_jitter` is confusing. Add `multiplier` param and deprecate `initial` as its alias. - Add `multiplier` parameter (default 1) - Emit `DeprecationWarning` when `initial` is used - Raise `ValueError` if both `initial` and `multiplier` are specified Co-Authored-By: Claude Opus 4.6 Change-Id: I63d23f41e58627085118b633a009c86ff2807f79 --- ...itial-for-multiplier-c7b4e2d9f1a83065.yaml | 10 ++++++++ tenacity/wait.py | 23 +++++++++++++++---- tests/test_tenacity.py | 20 +++++++++++++++- 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/deprecate-initial-for-multiplier-c7b4e2d9f1a83065.yaml diff --git a/releasenotes/notes/deprecate-initial-for-multiplier-c7b4e2d9f1a83065.yaml b/releasenotes/notes/deprecate-initial-for-multiplier-c7b4e2d9f1a83065.yaml new file mode 100644 index 00000000..e5588548 --- /dev/null +++ b/releasenotes/notes/deprecate-initial-for-multiplier-c7b4e2d9f1a83065.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + The ``initial`` parameter of ``wait_exponential_jitter`` is deprecated in + favor of ``multiplier``, for consistency with ``wait_exponential``. Passing + ``initial`` still works but emits a ``DeprecationWarning``. +features: + - | + Add ``multiplier`` parameter to ``wait_exponential_jitter``, consistent + with ``wait_exponential``. diff --git a/tenacity/wait.py b/tenacity/wait.py index 92fa1f4a..c7a430eb 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 @@ -240,12 +241,12 @@ def __call__(self, retry_state: "RetryCallState") -> float: class wait_exponential_jitter(wait_base): """Wait strategy that applies exponential backoff and jitter. - It allows for a customized initial wait, maximum wait, jitter and minimum. + It allows for a customized multiplier, maximum wait, jitter and minimum. This implements the strategy described here: https://cloud.google.com/storage/docs/retry-strategy - The wait time is max(min, min(initial * 2**n + random.uniform(0, jitter), maximum)) + The wait time is max(min, min(multiplier * 2**n + random.uniform(0, jitter), maximum)) where n is the retry count. """ @@ -256,8 +257,22 @@ def __init__( exp_base: float = 2, jitter: _utils.time_unit_type = 1, min: _utils.time_unit_type = 0, + multiplier: float = 1, ) -> None: - self.initial = initial + if initial != 1 and multiplier != 1: + raise ValueError( + "Cannot specify both 'initial' and 'multiplier' — use 'multiplier' only" + ) + + if initial != 1: + warnings.warn( + "The 'initial' parameter is deprecated, use 'multiplier' instead", + DeprecationWarning, + stacklevel=2, + ) + multiplier = initial + + self.multiplier = multiplier self.max = _utils.to_seconds(max) self.exp_base = exp_base self.jitter = _utils.to_seconds(jitter) @@ -267,7 +282,7 @@ 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 + result = self.multiplier * exp + jitter except OverflowError: result = self.max return max(max(0, self.min), min(result, self.max)) diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index 7061cd74..6a397392 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -609,7 +609,8 @@ def test_wait_exponential_jitter(self) -> None: 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) + with self.assertWarns(DeprecationWarning): + fn = tenacity.wait_exponential_jitter(10, 5) for _ in range(1000): self.assertEqual(fn(make_retry_state(1, 0)), 5) @@ -640,6 +641,23 @@ def test_wait_exponential_jitter_timedelta(self) -> None: self._assert_inclusive_range(fn(make_retry_state(5, 0)), 16, 17) self.assertEqual(fn(make_retry_state(7, 0)), 60) + def test_wait_exponential_jitter_multiplier(self) -> None: + fn = tenacity.wait_exponential_jitter(multiplier=10, max=60, jitter=0) + self.assertEqual(fn(make_retry_state(1, 0)), 10) + self.assertEqual(fn(make_retry_state(2, 0)), 20) + self.assertEqual(fn(make_retry_state(3, 0)), 40) + self.assertEqual(fn(make_retry_state(4, 0)), 60) + + def test_wait_exponential_jitter_initial_deprecated(self) -> None: + with self.assertWarns(DeprecationWarning): + fn = tenacity.wait_exponential_jitter(initial=10, max=60, jitter=0) + self.assertEqual(fn(make_retry_state(1, 0)), 10) + self.assertEqual(fn(make_retry_state(2, 0)), 20) + + def test_wait_exponential_jitter_initial_and_multiplier_raises(self) -> None: + with self.assertRaises(ValueError): + tenacity.wait_exponential_jitter(initial=5, multiplier=10) + def test_wait_retry_state_attributes(self) -> None: class ExtractCallState(Exception): pass