Skip to content

Commit d3383ec

Browse files
authored
Remove dependency on retry (#19)
* Add tests for retry_on_too_early * Replace retry lib with tenacity * Update changelog
1 parent 62def6a commit d3383ec

5 files changed

Lines changed: 85 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## UNRELEASED
44

5+
### Changed
6+
7+
- Removed the dependency on the library `retry` (and the indirect dependency on
8+
`py`), and replaced it with `tenacity`. This should be transparent.
9+
510
## v1.1.4 (2024-02-01)
611

712
### Fixed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import pytest
2+
3+
from ravenpackapi.exceptions import APIException, APIException425
4+
from ravenpackapi.upload.upload_utils import retry_on_too_early
5+
6+
7+
class TestRetryOnTooEarly:
8+
def test_fails_if_not_enough_retries(self):
9+
eventually_success = EventuallySuccess(num_calls_to_fail=10)
10+
with pytest.raises(APIException425):
11+
retry_on_too_early(eventually_success, 1, 2, 3, namearg="namevalue")
12+
assert eventually_success.num_calls == 10
13+
assert eventually_success.calls == [((1, 2, 3), {"namearg": "namevalue"})] * 10
14+
3
15+
min_wait, max_wait = 3 * 2 ** 9, 3 * 2 ** 10
16+
assert min_wait < self.seconds_slept <= max_wait
17+
18+
def test_succeeds_eventually(self):
19+
eventually_success = EventuallySuccess(num_calls_to_fail=9)
20+
retry_on_too_early(eventually_success, 1, 2, 3, namearg="namevalue")
21+
assert eventually_success.num_calls == 10
22+
assert eventually_success.calls == [((1, 2, 3), {"namearg": "namevalue"})] * 10
23+
24+
def test_only_handles_api_exception_425(self):
25+
eventually_success = EventuallySuccess(
26+
num_calls_to_fail=100, error_to_raise=APIException
27+
)
28+
with pytest.raises(APIException):
29+
retry_on_too_early(eventually_success, 1, 2, 3, namearg="namevalue")
30+
assert eventually_success.num_calls == 1
31+
assert eventually_success.calls == [((1, 2, 3), {"namearg": "namevalue"})]
32+
assert self.seconds_slept == 0
33+
34+
@pytest.fixture(autouse=True)
35+
def mocked_sleep(self, mocker):
36+
self.seconds_slept = 0
37+
38+
def sleep(seconds):
39+
self.seconds_slept += seconds
40+
41+
time = mocker.patch("tenacity.nap.time")
42+
time.sleep.side_effect = sleep
43+
return time.sleep
44+
45+
46+
class EventuallySuccess:
47+
def __init__(self, num_calls_to_fail=0, error_to_raise=APIException425):
48+
self.num_calls_to_fail = num_calls_to_fail
49+
self.num_calls = 0
50+
self.error_to_raise = error_to_raise
51+
self.calls = []
52+
53+
def __call__(self, *args, **kwargs):
54+
self.calls.append((args, kwargs))
55+
self.num_calls += 1
56+
if self.num_calls <= self.num_calls_to_fail:
57+
raise self.error_to_raise
58+
return self.num_calls
Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
import logging
22

3-
from retry.api import retry_call
3+
from tenacity import (
4+
retry,
5+
retry_if_exception_type,
6+
stop_after_attempt,
7+
wait_exponential_jitter,
8+
)
49

510
from ravenpackapi.exceptions import APIException425
611

712
logger = logging.getLogger("ravenpack.upload.retry")
813

914

1015
def retry_on_too_early(func, *args, **kwargs):
11-
response = retry_call(
12-
func,
13-
fargs=args,
14-
fkwargs=kwargs,
15-
exceptions=(APIException425,),
16-
delay=3,
17-
backoff=2,
18-
jitter=(1, 5),
19-
tries=10,
20-
logger=logger,
21-
)
22-
return response
16+
"""
17+
Retry a function that may raise a 425 API exception.
18+
19+
Note: This was originally implemented using the library "retry" and migrated
20+
to tenacity. It should not be used in new code, as the proper way to do this
21+
using tenacity is to decorate the funciton with @retry.
22+
"""
23+
wrapped_func = retry(
24+
retry=retry_if_exception_type(APIException425),
25+
wait=wait_exponential_jitter(initial=3, exp_base=2, jitter=5),
26+
stop=stop_after_attempt(10),
27+
reraise=True,
28+
)(func)
29+
return wrapped_func(*args, **kwargs)

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
requests[security]
22
six
33
python-dateutil
4-
retry
4+
tenacity

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,5 @@
3737
"License :: OSI Approved :: MIT License",
3838
],
3939
keywords="python analytics api rest news data",
40-
install_requires=["requests[security]", "python-dateutil", "six", "retry"],
40+
install_requires=["requests[security]", "python-dateutil", "six", "tenacity"],
4141
)

0 commit comments

Comments
 (0)