Skip to content

Commit

Permalink
fix: make retries
Browse files Browse the repository at this point in the history
  • Loading branch information
Ravencentric committed Mar 16, 2024
1 parent 4830e89 commit a6360c3
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 27 deletions.
26 changes: 16 additions & 10 deletions src/pyanilist/_clients/_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import httpx
from pydantic import validate_call
from tenacity import retry, stop_after_attempt, wait_incrementing
from tenacity import AsyncRetrying, stop_after_attempt, wait_incrementing

from .._enums import MediaFormat, MediaSeason, MediaStatus, MediaType
from .._models import Media
Expand All @@ -14,26 +14,26 @@


class AsyncAnilist:
def __init__(self, api_url: str = "https://graphql.anilist.co", **httpx_async_client_kwargs: Any) -> None:
def __init__(
self, api_url: str = "https://graphql.anilist.co", retries: int = 5, **httpx_async_client_kwargs: Any
) -> None:
"""
Async Anilist API client.
Parameters
----------
api_url : str, optional
The URL of the Anilist API. Default is "https://graphql.anilist.co".
retries : int, optional
Number of times to retry a failed request before raising an error. Default is 5.
httpx_async_client_kwargs : Any, optional
Keyword arguments to pass to the internal [httpx.AsyncClient()](https://www.python-httpx.org/api/#asyncclient)
used to make the POST request.
"""
self.api_url = api_url
self.retries = retries
self.httpx_async_client_kwargs = httpx_async_client_kwargs

@retry(
stop=stop_after_attempt(5),
wait=wait_incrementing(start=0, increment=1),
reraise=True,
)
async def _post_request(
self,
id: AnilistID | None = None,
Expand Down Expand Up @@ -92,9 +92,15 @@ async def _post_request(
"variables": {key: value for key, value in query_variables.items() if value is not None},
}

async with httpx.AsyncClient(**self.httpx_async_client_kwargs) as client:
response = await client.post(self.api_url, json=payload)
response.raise_for_status()
async for attempt in AsyncRetrying(
stop=stop_after_attempt(self.retries),
wait=wait_incrementing(start=0, increment=1),
reraise=True,
):
with attempt:
async with httpx.AsyncClient(**self.httpx_async_client_kwargs) as client:
response = await client.post(self.api_url, json=payload)
response.raise_for_status()

return response

Expand Down
24 changes: 15 additions & 9 deletions src/pyanilist/_clients/_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import httpx
from pydantic import validate_call
from tenacity import retry, stop_after_attempt, wait_incrementing
from tenacity import Retrying, stop_after_attempt, wait_incrementing

from .._enums import MediaFormat, MediaSeason, MediaStatus, MediaType
from .._models import Media
Expand All @@ -14,26 +14,26 @@


class Anilist:
def __init__(self, api_url: str = "https://graphql.anilist.co", **httpx_client_kwargs: Any) -> None:
def __init__(
self, api_url: str = "https://graphql.anilist.co", retries: int = 5, **httpx_client_kwargs: Any
) -> None:
"""
Anilist API client.
Parameters
----------
api_url : str, optional
The URL of the Anilist API. Default is "https://graphql.anilist.co".
retries : int, optional
Number of times to retry a failed request before raising an error. Default is 5.
httpx_client_kwargs : Any, optional
Keyword arguments to pass to the internal [httpx.Client()](https://www.python-httpx.org/api/#client)
used to make the POST request.
"""
self.api_url = api_url
self.retries = retries
self.httpx_client_kwargs = httpx_client_kwargs

@retry(
stop=stop_after_attempt(5),
wait=wait_incrementing(start=0, increment=1),
reraise=True,
)
def _post_request(
self,
id: AnilistID | None = None,
Expand Down Expand Up @@ -92,8 +92,14 @@ def _post_request(
"variables": {key: value for key, value in query_variables.items() if value is not None},
}

with httpx.Client(**self.httpx_client_kwargs) as client:
response = client.post(self.api_url, json=payload).raise_for_status()
for attempt in Retrying(
stop=stop_after_attempt(self.retries),
wait=wait_incrementing(start=0, increment=1),
reraise=True,
):
with attempt:
with httpx.Client(**self.httpx_client_kwargs) as client:
response = client.post(self.api_url, json=payload).raise_for_status()

return response

Expand Down
10 changes: 6 additions & 4 deletions tests/test_async_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
ValidationError,
)

anilist = AsyncAnilist(retries=1)


async def test_anilist_title_doesnt_exist() -> None:
with pytest.raises(HTTPStatusError, match="Not Found."):
await AsyncAnilist().search("Title does not exist", type=MediaType.MANGA)
await anilist.search("Title does not exist", type=MediaType.MANGA)


async def test_anilist_bad_search_combo() -> None:
with pytest.raises(HTTPStatusError, match="Not Found."):
await AsyncAnilist().search("Attack on titan", season_year=1999)
await anilist.search("Attack on titan", season_year=1999)


async def test_anilist_wrong_input_types() -> None:
with pytest.raises(ValidationError):
await AsyncAnilist().search(123456789, season_year="hello", type=True) # type: ignore
await anilist.search(123456789, season_year="hello", type=True) # type: ignore


async def test_anilist_bad_id() -> None:
with pytest.raises(HTTPStatusError, match="400 Bad Request"):
await AsyncAnilist().get(9999999999)
await anilist.get(9999999999)
10 changes: 6 additions & 4 deletions tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
ValidationError,
)

anilist = Anilist(retries=1)


def test_anilist_title_doesnt_exist() -> None:
with pytest.raises(HTTPStatusError, match="Not Found."):
Anilist().search("Title does not exist", type=MediaType.MANGA)
anilist.search("Title does not exist", type=MediaType.MANGA)


def test_anilist_bad_search_combo() -> None:
with pytest.raises(HTTPStatusError, match="Not Found."):
Anilist().search("Attack on titan", season_year=1999)
anilist.search("Attack on titan", season_year=1999)


def test_anilist_wrong_input_types() -> None:
with pytest.raises(ValidationError):
Anilist().search(123456789, season_year="hello", type=True) # type: ignore
anilist.search(123456789, season_year="hello", type=True) # type: ignore


def test_anilist_bad_id() -> None:
with pytest.raises(HTTPStatusError, match="400 Bad Request"):
Anilist().get(9999999999)
anilist.get(9999999999)

0 comments on commit a6360c3

Please sign in to comment.