Skip to content

Commit 3187492

Browse files
authored
Add ability to disable and enable retry logic (#296)
* Add ability to disable and enable retry logic * Typing * Linting
1 parent f2ad092 commit 3187492

2 files changed

Lines changed: 41 additions & 44 deletions

File tree

simplipy/api.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from datetime import datetime, timedelta
66
from json.decoder import JSONDecodeError
77
import sys
8-
from typing import TYPE_CHECKING, Any, Callable
8+
from typing import TYPE_CHECKING, Any, Callable, cast
99

1010
from aiohttp import ClientSession
1111
from aiohttp.client_exceptions import ClientResponseError
@@ -68,6 +68,7 @@ def __init__(
6868
) -> None:
6969
"""Initialize."""
7070
self._refresh_token_callbacks: list[Callable[..., None]] = []
71+
self._request_retries = request_retries
7172
self.session: ClientSession = session
7273

7374
# These will get filled in after initial authentication:
@@ -79,16 +80,7 @@ def __init__(
7980
self.user_id: int | None = None
8081
self.websocket: WebsocketClient | None = None
8182

82-
# Implement a version of the request coroutine, but with backoff/retry logic:
83-
self.async_request = backoff.on_exception(
84-
backoff.expo,
85-
ClientResponseError,
86-
jitter=backoff.random_jitter,
87-
logger=LOGGER,
88-
max_tries=request_retries,
89-
on_backoff=self._async_handle_on_backoff,
90-
on_giveup=self._handle_on_giveup,
91-
)(self._async_request)
83+
self.async_request = self._wrap_request_method(self._request_retries)
9284

9385
@classmethod
9486
async def async_from_auth(
@@ -264,6 +256,29 @@ def _handle_on_giveup(_: dict[str, Any]) -> None:
264256
err = err_info[1].with_traceback(err_info[2]) # type: ignore
265257
raise RequestError(err) from err
266258

259+
def _wrap_request_method(self, request_retries: int) -> Callable:
260+
"""Wrap the request method in backoff/retry logic."""
261+
return cast(
262+
Callable,
263+
backoff.on_exception(
264+
backoff.expo,
265+
ClientResponseError,
266+
jitter=backoff.random_jitter,
267+
logger=LOGGER,
268+
max_tries=request_retries,
269+
on_backoff=self._async_handle_on_backoff,
270+
on_giveup=self._handle_on_giveup,
271+
)(self._async_request),
272+
)
273+
274+
def disable_request_retries(self) -> None:
275+
"""Disable the request retry mechanism."""
276+
self.async_request = self._wrap_request_method(1)
277+
278+
def enable_request_retries(self) -> None:
279+
"""Enable the request retry mechanism."""
280+
self.async_request = self._wrap_request_method(self._request_retries)
281+
267282
def add_refresh_token_callback(
268283
self, callback: Callable[..., None]
269284
) -> Callable[..., None]:

tests/test_api.py

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -278,45 +278,20 @@ async def test_refresh_token_callback(
278278

279279

280280
@pytest.mark.asyncio
281-
async def test_request_error_failed_retry(aresponses, server):
282-
"""Test that a RequestError that fails multiple times still raises."""
283-
server.add(
284-
"api.simplisafe.com",
285-
f"/v1/users/{TEST_SUBSCRIPTION_ID}/subscriptions",
286-
"get",
287-
response=aresponses.Response(text="Conflict", status=409),
288-
)
289-
server.add(
290-
"api.simplisafe.com",
291-
f"/v1/users/{TEST_SUBSCRIPTION_ID}/subscriptions",
292-
"get",
293-
response=aresponses.Response(text="Conflict", status=409),
294-
)
295-
296-
async with aiohttp.ClientSession() as session:
297-
simplisafe = await API.async_from_auth(
298-
TEST_AUTHORIZATION_CODE,
299-
TEST_CODE_VERIFIER,
300-
session=session,
301-
# Set so that our tests don't take too long:
302-
request_retries=1,
303-
)
304-
305-
with pytest.raises(RequestError):
306-
await simplisafe.async_get_systems()
307-
308-
aresponses.assert_plan_strictly_followed()
309-
310-
311-
@pytest.mark.asyncio
312-
async def test_request_error_successful_retry(
281+
async def test_request_retry(
313282
api_token_response,
314283
aresponses,
315284
server,
316285
v2_settings_response,
317286
v2_subscriptions_response,
318287
):
319-
"""Test that a RequestError can be successfully retried."""
288+
"""Test that request retries work."""
289+
server.add(
290+
"api.simplisafe.com",
291+
f"/v1/users/{TEST_SUBSCRIPTION_ID}/subscriptions",
292+
"get",
293+
response=aresponses.Response(text="Conflict", status=409),
294+
)
320295
server.add(
321296
"api.simplisafe.com",
322297
f"/v1/users/{TEST_SUBSCRIPTION_ID}/subscriptions",
@@ -349,6 +324,13 @@ async def test_request_error_successful_retry(
349324
TEST_AUTHORIZATION_CODE, TEST_CODE_VERIFIER, session=session
350325
)
351326

327+
simplisafe.disable_request_retries()
328+
329+
with pytest.raises(RequestError):
330+
await simplisafe.async_get_systems()
331+
332+
simplisafe.enable_request_retries()
333+
352334
# If this succeeds without throwing an exception, the retry is successful:
353335
await simplisafe.async_get_systems()
354336

0 commit comments

Comments
 (0)