Skip to content

Commit e49665e

Browse files
PatrykGalaKrzysztof Godlewski
andauthored
feat: Retry http requests in auth flow (#49)
* feat: Retry http requests in auth flow * Update src/neptune_api/client.py Co-authored-by: Krzysztof Godlewski <krzysztof.godlewski@neptune.ai> --------- Co-authored-by: Krzysztof Godlewski <krzysztof.godlewski@neptune.ai>
1 parent 402be2b commit e49665e

File tree

2 files changed

+18
-9
lines changed

2 files changed

+18
-9
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ attrs = ">=21.3.0"
1515
python-dateutil = "^2.8.0"
1616
PyJWT = "^2.0.0"
1717
protobuf = ">=4,<6"
18+
backoff = "^2.2.1"
1819

1920
[tool.poetry]
2021
name = "neptune-api"
@@ -95,3 +96,4 @@ check_untyped_defs = "True"
9596
warn_return_any = "True"
9697
show_error_codes = "True"
9798
warn_unused_ignores = "True"
99+
ignore_missing_imports = "True"

src/neptune_api/client.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
__all__ = ["Client", "AuthenticatedClient", "NeptuneAuthenticator"]
22

3+
import logging
34
import ssl
45
import threading
56
import typing
@@ -12,6 +13,7 @@
1213
Union,
1314
)
1415

16+
import backoff
1517
import httpx
1618
from attrs import (
1719
define,
@@ -23,6 +25,9 @@
2325
from neptune_api.errors import UnableToRefreshTokenError
2426
from neptune_api.types import OAuthToken
2527

28+
# Disable httpx logging, httpx logs requests at INFO level
29+
logging.getLogger("httpx").setLevel(logging.WARN)
30+
2631

2732
@define
2833
class Client:
@@ -349,12 +354,9 @@ def __init__(
349354
self._client = client
350355
self._token: Optional[OAuthToken] = None
351356

352-
def _refresh_existing_token(self) -> None:
357+
def _refresh_existing_token(self) -> OAuthToken:
353358
if self._token is None:
354-
# This should never happen, but just in case
355-
self._token = self._api_key_exchange_factory(self._client, self._credentials)
356-
return
357-
359+
raise ValueError("Cannot refresh an empty token")
358360
try:
359361
response = self._client.get_httpx_client().post(
360362
url=self._token_refreshing_endpoint,
@@ -366,21 +368,26 @@ def _refresh_existing_token(self) -> None:
366368
},
367369
)
368370
data = response.json()
371+
return OAuthToken.from_tokens(access=data["access_token"], refresh=data["refresh_token"])
369372
except Exception as e:
370373
raise UnableToRefreshTokenError("Unable to refresh token") from e
371374

372-
self._token = OAuthToken.from_tokens(access=data["access_token"], refresh=data["refresh_token"])
373-
375+
@backoff.on_exception(backoff.expo, Exception, max_time=30, max_tries=3)
374376
def _refresh_token(self) -> None:
375377
with self.__LOCK:
376378
if self._token is not None:
377-
self._refresh_existing_token()
379+
self._token = self._refresh_existing_token()
378380

379381
if self._token is None:
380382
self._token = self._api_key_exchange_factory(self._client, self._credentials)
381383

382384
def _refresh_token_if_expired(self) -> None:
383-
if self._token is None or self._token.is_expired:
385+
try:
386+
if self._token is None or self._token.is_expired:
387+
self._refresh_token()
388+
except Exception:
389+
# Reset the token to None to force a new token retrieval
390+
self._token = None
384391
self._refresh_token()
385392

386393
def sync_auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:

0 commit comments

Comments
 (0)