Skip to content

Commit 416203c

Browse files
fix: Retry constructors methods support None (#592)
1 parent b517bf4 commit 416203c

File tree

3 files changed

+77
-49
lines changed

3 files changed

+77
-49
lines changed

google/api_core/retry/retry_base.py

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import time
2626

2727
from enum import Enum
28-
from typing import Any, Callable, TYPE_CHECKING
28+
from typing import Any, Callable, Optional, TYPE_CHECKING
2929

3030
import requests.exceptions
3131

@@ -238,8 +238,8 @@ def __init__(
238238
initial: float = _DEFAULT_INITIAL_DELAY,
239239
maximum: float = _DEFAULT_MAXIMUM_DELAY,
240240
multiplier: float = _DEFAULT_DELAY_MULTIPLIER,
241-
timeout: float = _DEFAULT_DEADLINE,
242-
on_error: Callable[[Exception], Any] | None = None,
241+
timeout: Optional[float] = _DEFAULT_DEADLINE,
242+
on_error: Optional[Callable[[Exception], Any]] = None,
243243
**kwargs: Any,
244244
) -> None:
245245
self._predicate = predicate
@@ -265,48 +265,39 @@ def deadline(self) -> float | None:
265265
def timeout(self) -> float | None:
266266
return self._timeout
267267

268-
def _replace(
269-
self,
270-
predicate: Callable[[Exception], bool] | None = None,
271-
initial: float | None = None,
272-
maximum: float | None = None,
273-
multiplier: float | None = None,
274-
timeout: float | None = None,
275-
on_error: Callable[[Exception], Any] | None = None,
276-
) -> Self:
277-
return type(self)(
278-
predicate=predicate or self._predicate,
279-
initial=initial or self._initial,
280-
maximum=maximum or self._maximum,
281-
multiplier=multiplier or self._multiplier,
282-
timeout=timeout or self._timeout,
283-
on_error=on_error or self._on_error,
284-
)
285-
286268
def with_deadline(self, deadline: float | None) -> Self:
287269
"""Return a copy of this retry with the given timeout.
288270
289271
DEPRECATED: use :meth:`with_timeout` instead. Refer to the ``Retry`` class
290272
documentation for details.
291273
292274
Args:
293-
deadline (float): How long to keep retrying, in seconds.
275+
deadline (float|None): How long to keep retrying, in seconds. If None,
276+
no timeout is enforced.
294277
295278
Returns:
296279
Retry: A new retry instance with the given timeout.
297280
"""
298-
return self._replace(timeout=deadline)
281+
return self.with_timeout(deadline)
299282

300-
def with_timeout(self, timeout: float) -> Self:
283+
def with_timeout(self, timeout: float | None) -> Self:
301284
"""Return a copy of this retry with the given timeout.
302285
303286
Args:
304-
timeout (float): How long to keep retrying, in seconds.
287+
timeout (float): How long to keep retrying, in seconds. If None,
288+
no timeout will be enforced.
305289
306290
Returns:
307291
Retry: A new retry instance with the given timeout.
308292
"""
309-
return self._replace(timeout=timeout)
293+
return type(self)(
294+
predicate=self._predicate,
295+
initial=self._initial,
296+
maximum=self._maximum,
297+
multiplier=self._multiplier,
298+
timeout=timeout,
299+
on_error=self._on_error,
300+
)
310301

311302
def with_predicate(self, predicate: Callable[[Exception], bool]) -> Self:
312303
"""Return a copy of this retry with the given predicate.
@@ -318,26 +309,42 @@ def with_predicate(self, predicate: Callable[[Exception], bool]) -> Self:
318309
Returns:
319310
Retry: A new retry instance with the given predicate.
320311
"""
321-
return self._replace(predicate=predicate)
312+
return type(self)(
313+
predicate=predicate,
314+
initial=self._initial,
315+
maximum=self._maximum,
316+
multiplier=self._multiplier,
317+
timeout=self._timeout,
318+
on_error=self._on_error,
319+
)
322320

323321
def with_delay(
324322
self,
325-
initial: float | None = None,
326-
maximum: float | None = None,
327-
multiplier: float | None = None,
323+
initial: Optional[float] = None,
324+
maximum: Optional[float] = None,
325+
multiplier: Optional[float] = None,
328326
) -> Self:
329327
"""Return a copy of this retry with the given delay options.
330328
331329
Args:
332330
initial (float): The minimum amount of time to delay (in seconds). This must
333-
be greater than 0.
334-
maximum (float): The maximum amount of time to delay (in seconds).
335-
multiplier (float): The multiplier applied to the delay.
331+
be greater than 0. If None, the current value is used.
332+
maximum (float): The maximum amount of time to delay (in seconds). If None, the
333+
current value is used.
334+
multiplier (float): The multiplier applied to the delay. If None, the current
335+
value is used.
336336
337337
Returns:
338-
Retry: A new retry instance with the given predicate.
338+
Retry: A new retry instance with the given delay options.
339339
"""
340-
return self._replace(initial=initial, maximum=maximum, multiplier=multiplier)
340+
return type(self)(
341+
predicate=self._predicate,
342+
initial=initial if initial is not None else self._initial,
343+
maximum=maximum if maximum is not None else self._maximum,
344+
multiplier=multiplier if multiplier is not None else self._multiplier,
345+
timeout=self._timeout,
346+
on_error=self._on_error,
347+
)
341348

342349
def __str__(self) -> str:
343350
return (

google/api_core/retry/retry_unary.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class Retry(_BaseRetry):
251251
must be greater than 0.
252252
maximum (float): The maximum amount of time to delay in seconds.
253253
multiplier (float): The multiplier applied to the delay.
254-
timeout (float): How long to keep retrying, in seconds.
254+
timeout (Optional[float]): How long to keep retrying, in seconds.
255255
Note: timeout is only checked before initiating a retry, so the target may
256256
run past the timeout value as long as it is healthy.
257257
on_error (Callable[Exception]): A function to call while processing

tests/unit/retry/test_retry_base.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ def test_constructor_options(self):
138138
assert retry_._on_error is _some_function
139139

140140
@pytest.mark.parametrize("use_deadline", [True, False])
141-
def test_with_timeout(self, use_deadline):
141+
@pytest.mark.parametrize("value", [None, 0, 1, 4, 42, 5.5])
142+
def test_with_timeout(self, use_deadline, value):
142143
retry_ = self._make_one(
143144
predicate=mock.sentinel.predicate,
144145
initial=1,
@@ -148,11 +149,17 @@ def test_with_timeout(self, use_deadline):
148149
on_error=mock.sentinel.on_error,
149150
)
150151
new_retry = (
151-
retry_.with_timeout(42) if not use_deadline else retry_.with_deadline(42)
152+
retry_.with_timeout(value)
153+
if not use_deadline
154+
else retry_.with_deadline(value)
152155
)
153156
assert retry_ is not new_retry
154-
assert new_retry._timeout == 42
155-
assert new_retry.timeout == 42 if not use_deadline else new_retry.deadline == 42
157+
assert new_retry._timeout == value
158+
assert (
159+
new_retry.timeout == value
160+
if not use_deadline
161+
else new_retry.deadline == value
162+
)
156163

157164
# the rest of the attributes should remain the same
158165
assert new_retry._predicate is retry_._predicate
@@ -196,20 +203,34 @@ def test_with_delay_noop(self):
196203
assert new_retry._maximum == retry_._maximum
197204
assert new_retry._multiplier == retry_._multiplier
198205

199-
def test_with_delay(self):
206+
@pytest.mark.parametrize(
207+
"originals,updated,expected",
208+
[
209+
[(1, 2, 3), (4, 5, 6), (4, 5, 6)],
210+
[(1, 2, 3), (0, 0, 0), (0, 0, 0)],
211+
[(1, 2, 3), (None, None, None), (1, 2, 3)],
212+
[(0, 0, 0), (None, None, None), (0, 0, 0)],
213+
[(1, 2, 3), (None, 0.5, None), (1, 0.5, 3)],
214+
[(1, 2, 3), (None, 0.5, 4), (1, 0.5, 4)],
215+
[(1, 2, 3), (9, None, None), (9, 2, 3)],
216+
],
217+
)
218+
def test_with_delay(self, originals, updated, expected):
200219
retry_ = self._make_one(
201220
predicate=mock.sentinel.predicate,
202-
initial=1,
203-
maximum=2,
204-
multiplier=3,
205-
timeout=4,
221+
initial=originals[0],
222+
maximum=originals[1],
223+
multiplier=originals[2],
224+
timeout=14,
206225
on_error=mock.sentinel.on_error,
207226
)
208-
new_retry = retry_.with_delay(initial=5, maximum=6, multiplier=7)
227+
new_retry = retry_.with_delay(
228+
initial=updated[0], maximum=updated[1], multiplier=updated[2]
229+
)
209230
assert retry_ is not new_retry
210-
assert new_retry._initial == 5
211-
assert new_retry._maximum == 6
212-
assert new_retry._multiplier == 7
231+
assert new_retry._initial == expected[0]
232+
assert new_retry._maximum == expected[1]
233+
assert new_retry._multiplier == expected[2]
213234

214235
# the rest of the attributes should remain the same
215236
assert new_retry._timeout == retry_._timeout

0 commit comments

Comments
 (0)