From b8eab7e6dd37c8b3fd622eedbe6426cc862b2845 Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:00:30 +0530 Subject: [PATCH 01/10] ci: add sleep to hopefully deal with http 429 --- tests/test_anilist.py | 11 +++++++++++ tests/test_async_anilist.py | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/test_anilist.py b/tests/test_anilist.py index 46a04ce..e88fba6 100644 --- a/tests/test_anilist.py +++ b/tests/test_anilist.py @@ -1,3 +1,5 @@ +import time + import pytest from pyanilist import ( Anilist, @@ -13,6 +15,7 @@ def test_anilist() -> None: + time.sleep(5) media = Anilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -22,6 +25,7 @@ def test_anilist() -> None: def test_anilist_with_type_constraint() -> None: + time.sleep(5) media = Anilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -31,6 +35,7 @@ def test_anilist_with_type_constraint() -> None: def test_anilist_with_some_constraints() -> None: + time.sleep(5) media = Anilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -42,6 +47,7 @@ def test_anilist_with_some_constraints() -> None: def test_anilist_with_all_constraints() -> None: + time.sleep(5) media = Anilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -58,21 +64,25 @@ def test_anilist_with_all_constraints() -> None: def test_anilist_title_doesnt_exist() -> None: + time.sleep(5) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Title does not exist", type=MediaType.MANGA) def test_anilist_bad_search_combo() -> None: + time.sleep(5) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Attack on titan", season_year=1999) def test_anilist_wrong_input_types() -> None: + time.sleep(5) with pytest.raises(ValidationError): Anilist().search(123456789, season_year="hello", type=True) # type: ignore def test_anilist_id() -> None: + time.sleep(5) media = Anilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -82,5 +92,6 @@ def test_anilist_id() -> None: def test_anilist_bad_id() -> None: + time.sleep(5) with pytest.raises(HTTPStatusError, match="400 Bad Request"): Anilist().get(9999999999) diff --git a/tests/test_async_anilist.py b/tests/test_async_anilist.py index 0d89aad..90ba4ad 100644 --- a/tests/test_async_anilist.py +++ b/tests/test_async_anilist.py @@ -1,3 +1,5 @@ +import time + import pytest from pyanilist import ( AsyncAnilist, @@ -13,6 +15,7 @@ async def test_anilist() -> None: + time.sleep(5) media = await AsyncAnilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -22,6 +25,7 @@ async def test_anilist() -> None: async def test_anilist_with_type_constraint() -> None: + time.sleep(5) media = await AsyncAnilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -31,6 +35,7 @@ async def test_anilist_with_type_constraint() -> None: async def test_anilist_with_some_constraints() -> None: + time.sleep(5) media = await AsyncAnilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -42,6 +47,7 @@ async def test_anilist_with_some_constraints() -> None: async def test_anilist_with_all_constraints() -> None: + time.sleep(5) media = await AsyncAnilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -58,21 +64,25 @@ async def test_anilist_with_all_constraints() -> None: async def test_anilist_title_doesnt_exist() -> None: + time.sleep(5) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Title does not exist", type=MediaType.MANGA) async def test_anilist_bad_search_combo() -> None: + time.sleep(5) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Attack on titan", season_year=1999) async def test_anilist_wrong_input_types() -> None: + time.sleep(5) with pytest.raises(ValidationError): await AsyncAnilist().search(123456789, season_year="hello", type=True) # type: ignore async def test_anilist_id() -> None: + time.sleep(5) media = await AsyncAnilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -82,5 +92,6 @@ async def test_anilist_id() -> None: async def test_anilist_bad_id() -> None: + time.sleep(5) with pytest.raises(HTTPStatusError, match="400 Bad Request"): await AsyncAnilist().get(9999999999) From f6df17cf7e1958f8074122b89581303e52e3f88b Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:12:13 +0530 Subject: [PATCH 02/10] fix: too slow --- .github/workflows/test.yml | 1 - tests/test_anilist.py | 18 +++++++++--------- tests/test_async_anilist.py | 18 +++++++++--------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 596141e..d2f25dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,6 @@ jobs: test: name: Tests strategy: - max-parallel: 1 fail-fast: false matrix: python-version: ['3.9', '3.10', '3.11', '3.12'] diff --git a/tests/test_anilist.py b/tests/test_anilist.py index e88fba6..2468e8d 100644 --- a/tests/test_anilist.py +++ b/tests/test_anilist.py @@ -15,7 +15,7 @@ def test_anilist() -> None: - time.sleep(5) + time.sleep(1) media = Anilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +25,7 @@ def test_anilist() -> None: def test_anilist_with_type_constraint() -> None: - time.sleep(5) + time.sleep(1) media = Anilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +35,7 @@ def test_anilist_with_type_constraint() -> None: def test_anilist_with_some_constraints() -> None: - time.sleep(5) + time.sleep(1) media = Anilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +47,7 @@ def test_anilist_with_some_constraints() -> None: def test_anilist_with_all_constraints() -> None: - time.sleep(5) + time.sleep(1) media = Anilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -64,25 +64,25 @@ def test_anilist_with_all_constraints() -> None: def test_anilist_title_doesnt_exist() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Title does not exist", type=MediaType.MANGA) def test_anilist_bad_search_combo() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Attack on titan", season_year=1999) def test_anilist_wrong_input_types() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(ValidationError): Anilist().search(123456789, season_year="hello", type=True) # type: ignore def test_anilist_id() -> None: - time.sleep(5) + time.sleep(1) media = Anilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -92,6 +92,6 @@ def test_anilist_id() -> None: def test_anilist_bad_id() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(HTTPStatusError, match="400 Bad Request"): Anilist().get(9999999999) diff --git a/tests/test_async_anilist.py b/tests/test_async_anilist.py index 90ba4ad..92f60f0 100644 --- a/tests/test_async_anilist.py +++ b/tests/test_async_anilist.py @@ -15,7 +15,7 @@ async def test_anilist() -> None: - time.sleep(5) + time.sleep(1) media = await AsyncAnilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +25,7 @@ async def test_anilist() -> None: async def test_anilist_with_type_constraint() -> None: - time.sleep(5) + time.sleep(1) media = await AsyncAnilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +35,7 @@ async def test_anilist_with_type_constraint() -> None: async def test_anilist_with_some_constraints() -> None: - time.sleep(5) + time.sleep(1) media = await AsyncAnilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +47,7 @@ async def test_anilist_with_some_constraints() -> None: async def test_anilist_with_all_constraints() -> None: - time.sleep(5) + time.sleep(1) media = await AsyncAnilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -64,25 +64,25 @@ async def test_anilist_with_all_constraints() -> None: async def test_anilist_title_doesnt_exist() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Title does not exist", type=MediaType.MANGA) async def test_anilist_bad_search_combo() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Attack on titan", season_year=1999) async def test_anilist_wrong_input_types() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(ValidationError): await AsyncAnilist().search(123456789, season_year="hello", type=True) # type: ignore async def test_anilist_id() -> None: - time.sleep(5) + time.sleep(1) media = await AsyncAnilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -92,6 +92,6 @@ async def test_anilist_id() -> None: async def test_anilist_bad_id() -> None: - time.sleep(5) + time.sleep(1) with pytest.raises(HTTPStatusError, match="400 Bad Request"): await AsyncAnilist().get(9999999999) From a91fb8f6086cd70f50d1623fb882660464f11202 Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:16:17 +0530 Subject: [PATCH 03/10] ci: 2 seconds --- tests/test_anilist.py | 18 +++++++++--------- tests/test_async_anilist.py | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_anilist.py b/tests/test_anilist.py index 2468e8d..2feb2b4 100644 --- a/tests/test_anilist.py +++ b/tests/test_anilist.py @@ -15,7 +15,7 @@ def test_anilist() -> None: - time.sleep(1) + time.sleep(2) media = Anilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +25,7 @@ def test_anilist() -> None: def test_anilist_with_type_constraint() -> None: - time.sleep(1) + time.sleep(2) media = Anilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +35,7 @@ def test_anilist_with_type_constraint() -> None: def test_anilist_with_some_constraints() -> None: - time.sleep(1) + time.sleep(2) media = Anilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +47,7 @@ def test_anilist_with_some_constraints() -> None: def test_anilist_with_all_constraints() -> None: - time.sleep(1) + time.sleep(2) media = Anilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -64,25 +64,25 @@ def test_anilist_with_all_constraints() -> None: def test_anilist_title_doesnt_exist() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Title does not exist", type=MediaType.MANGA) def test_anilist_bad_search_combo() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Attack on titan", season_year=1999) def test_anilist_wrong_input_types() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(ValidationError): Anilist().search(123456789, season_year="hello", type=True) # type: ignore def test_anilist_id() -> None: - time.sleep(1) + time.sleep(2) media = Anilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -92,6 +92,6 @@ def test_anilist_id() -> None: def test_anilist_bad_id() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(HTTPStatusError, match="400 Bad Request"): Anilist().get(9999999999) diff --git a/tests/test_async_anilist.py b/tests/test_async_anilist.py index 92f60f0..d07c7b7 100644 --- a/tests/test_async_anilist.py +++ b/tests/test_async_anilist.py @@ -15,7 +15,7 @@ async def test_anilist() -> None: - time.sleep(1) + time.sleep(2) media = await AsyncAnilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +25,7 @@ async def test_anilist() -> None: async def test_anilist_with_type_constraint() -> None: - time.sleep(1) + time.sleep(2) media = await AsyncAnilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +35,7 @@ async def test_anilist_with_type_constraint() -> None: async def test_anilist_with_some_constraints() -> None: - time.sleep(1) + time.sleep(2) media = await AsyncAnilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +47,7 @@ async def test_anilist_with_some_constraints() -> None: async def test_anilist_with_all_constraints() -> None: - time.sleep(1) + time.sleep(2) media = await AsyncAnilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -64,25 +64,25 @@ async def test_anilist_with_all_constraints() -> None: async def test_anilist_title_doesnt_exist() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Title does not exist", type=MediaType.MANGA) async def test_anilist_bad_search_combo() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Attack on titan", season_year=1999) async def test_anilist_wrong_input_types() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(ValidationError): await AsyncAnilist().search(123456789, season_year="hello", type=True) # type: ignore async def test_anilist_id() -> None: - time.sleep(1) + time.sleep(2) media = await AsyncAnilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -92,6 +92,6 @@ async def test_anilist_id() -> None: async def test_anilist_bad_id() -> None: - time.sleep(1) + time.sleep(2) with pytest.raises(HTTPStatusError, match="400 Bad Request"): await AsyncAnilist().get(9999999999) From ad45ea70a0d14d7577fb4a6539a2a5a662f0d31d Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:20:10 +0530 Subject: [PATCH 04/10] ci: 3 seconds, it's joever --- tests/test_anilist.py | 18 +++++++++--------- tests/test_async_anilist.py | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_anilist.py b/tests/test_anilist.py index 2feb2b4..3732275 100644 --- a/tests/test_anilist.py +++ b/tests/test_anilist.py @@ -15,7 +15,7 @@ def test_anilist() -> None: - time.sleep(2) + time.sleep(3) media = Anilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +25,7 @@ def test_anilist() -> None: def test_anilist_with_type_constraint() -> None: - time.sleep(2) + time.sleep(3) media = Anilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +35,7 @@ def test_anilist_with_type_constraint() -> None: def test_anilist_with_some_constraints() -> None: - time.sleep(2) + time.sleep(3) media = Anilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +47,7 @@ def test_anilist_with_some_constraints() -> None: def test_anilist_with_all_constraints() -> None: - time.sleep(2) + time.sleep(3) media = Anilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -64,25 +64,25 @@ def test_anilist_with_all_constraints() -> None: def test_anilist_title_doesnt_exist() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Title does not exist", type=MediaType.MANGA) def test_anilist_bad_search_combo() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(HTTPStatusError, match="Not Found."): Anilist().search("Attack on titan", season_year=1999) def test_anilist_wrong_input_types() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(ValidationError): Anilist().search(123456789, season_year="hello", type=True) # type: ignore def test_anilist_id() -> None: - time.sleep(2) + time.sleep(3) media = Anilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -92,6 +92,6 @@ def test_anilist_id() -> None: def test_anilist_bad_id() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(HTTPStatusError, match="400 Bad Request"): Anilist().get(9999999999) diff --git a/tests/test_async_anilist.py b/tests/test_async_anilist.py index d07c7b7..c015a42 100644 --- a/tests/test_async_anilist.py +++ b/tests/test_async_anilist.py @@ -15,7 +15,7 @@ async def test_anilist() -> None: - time.sleep(2) + time.sleep(3) media = await AsyncAnilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +25,7 @@ async def test_anilist() -> None: async def test_anilist_with_type_constraint() -> None: - time.sleep(2) + time.sleep(3) media = await AsyncAnilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +35,7 @@ async def test_anilist_with_type_constraint() -> None: async def test_anilist_with_some_constraints() -> None: - time.sleep(2) + time.sleep(3) media = await AsyncAnilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +47,7 @@ async def test_anilist_with_some_constraints() -> None: async def test_anilist_with_all_constraints() -> None: - time.sleep(2) + time.sleep(3) media = await AsyncAnilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -64,25 +64,25 @@ async def test_anilist_with_all_constraints() -> None: async def test_anilist_title_doesnt_exist() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Title does not exist", type=MediaType.MANGA) async def test_anilist_bad_search_combo() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(HTTPStatusError, match="Not Found."): await AsyncAnilist().search("Attack on titan", season_year=1999) async def test_anilist_wrong_input_types() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(ValidationError): await AsyncAnilist().search(123456789, season_year="hello", type=True) # type: ignore async def test_anilist_id() -> None: - time.sleep(2) + time.sleep(3) media = await AsyncAnilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -92,6 +92,6 @@ async def test_anilist_id() -> None: async def test_anilist_bad_id() -> None: - time.sleep(2) + time.sleep(3) with pytest.raises(HTTPStatusError, match="400 Bad Request"): await AsyncAnilist().get(9999999999) From ff62714470988fe92a3a01e9e123a06df01ea9ea Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:27:13 +0530 Subject: [PATCH 05/10] fix: seperate test files --- tests/test_anilist.py | 34 ---------------------------------- tests/test_async_anilist.py | 34 ---------------------------------- tests/test_async_exceptions.py | 27 +++++++++++++++++++++++++++ tests/test_exceptions.py | 27 +++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 68 deletions(-) create mode 100644 tests/test_async_exceptions.py create mode 100644 tests/test_exceptions.py diff --git a/tests/test_anilist.py b/tests/test_anilist.py index 3732275..b5d8848 100644 --- a/tests/test_anilist.py +++ b/tests/test_anilist.py @@ -1,21 +1,15 @@ -import time - -import pytest from pyanilist import ( Anilist, - HTTPStatusError, HttpUrl, MediaFormat, MediaSeason, MediaSource, MediaStatus, MediaType, - ValidationError, ) def test_anilist() -> None: - time.sleep(3) media = Anilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +19,6 @@ def test_anilist() -> None: def test_anilist_with_type_constraint() -> None: - time.sleep(3) media = Anilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +28,6 @@ def test_anilist_with_type_constraint() -> None: def test_anilist_with_some_constraints() -> None: - time.sleep(3) media = Anilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +39,6 @@ def test_anilist_with_some_constraints() -> None: def test_anilist_with_all_constraints() -> None: - time.sleep(3) media = Anilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -63,35 +54,10 @@ def test_anilist_with_all_constraints() -> None: assert media.site_url == HttpUrl("https://anilist.co/anime/21459") -def test_anilist_title_doesnt_exist() -> None: - time.sleep(3) - with pytest.raises(HTTPStatusError, match="Not Found."): - Anilist().search("Title does not exist", type=MediaType.MANGA) - - -def test_anilist_bad_search_combo() -> None: - time.sleep(3) - with pytest.raises(HTTPStatusError, match="Not Found."): - Anilist().search("Attack on titan", season_year=1999) - - -def test_anilist_wrong_input_types() -> None: - time.sleep(3) - with pytest.raises(ValidationError): - Anilist().search(123456789, season_year="hello", type=True) # type: ignore - - def test_anilist_id() -> None: - time.sleep(3) media = Anilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 assert media.source == MediaSource.MANGA assert media.type == MediaType.ANIME assert media.site_url == HttpUrl("https://anilist.co/anime/16498") - - -def test_anilist_bad_id() -> None: - time.sleep(3) - with pytest.raises(HTTPStatusError, match="400 Bad Request"): - Anilist().get(9999999999) diff --git a/tests/test_async_anilist.py b/tests/test_async_anilist.py index c015a42..b8266f1 100644 --- a/tests/test_async_anilist.py +++ b/tests/test_async_anilist.py @@ -1,21 +1,15 @@ -import time - -import pytest from pyanilist import ( AsyncAnilist, - HTTPStatusError, HttpUrl, MediaFormat, MediaSeason, MediaSource, MediaStatus, MediaType, - ValidationError, ) async def test_anilist() -> None: - time.sleep(3) media = await AsyncAnilist().search("Attack on titan") assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 @@ -25,7 +19,6 @@ async def test_anilist() -> None: async def test_anilist_with_type_constraint() -> None: - time.sleep(3) media = await AsyncAnilist().search("Attack on titan", type=MediaType.MANGA) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2009 @@ -35,7 +28,6 @@ async def test_anilist_with_type_constraint() -> None: async def test_anilist_with_some_constraints() -> None: - time.sleep(3) media = await AsyncAnilist().search( "violet evergarden", type=MediaType.MANGA, format=MediaFormat.NOVEL, status=MediaStatus.FINISHED ) @@ -47,7 +39,6 @@ async def test_anilist_with_some_constraints() -> None: async def test_anilist_with_all_constraints() -> None: - time.sleep(3) media = await AsyncAnilist().search( "My Hero Academia", season=MediaSeason.SPRING, @@ -63,35 +54,10 @@ async def test_anilist_with_all_constraints() -> None: assert media.site_url == HttpUrl("https://anilist.co/anime/21459") -async def test_anilist_title_doesnt_exist() -> None: - time.sleep(3) - with pytest.raises(HTTPStatusError, match="Not Found."): - await AsyncAnilist().search("Title does not exist", type=MediaType.MANGA) - - -async def test_anilist_bad_search_combo() -> None: - time.sleep(3) - with pytest.raises(HTTPStatusError, match="Not Found."): - await AsyncAnilist().search("Attack on titan", season_year=1999) - - -async def test_anilist_wrong_input_types() -> None: - time.sleep(3) - with pytest.raises(ValidationError): - await AsyncAnilist().search(123456789, season_year="hello", type=True) # type: ignore - - async def test_anilist_id() -> None: - time.sleep(3) media = await AsyncAnilist().get(16498) assert media.title.romaji == "Shingeki no Kyojin" assert media.start_date.year == 2013 assert media.source == MediaSource.MANGA assert media.type == MediaType.ANIME assert media.site_url == HttpUrl("https://anilist.co/anime/16498") - - -async def test_anilist_bad_id() -> None: - time.sleep(3) - with pytest.raises(HTTPStatusError, match="400 Bad Request"): - await AsyncAnilist().get(9999999999) diff --git a/tests/test_async_exceptions.py b/tests/test_async_exceptions.py new file mode 100644 index 0000000..c0c6139 --- /dev/null +++ b/tests/test_async_exceptions.py @@ -0,0 +1,27 @@ +import pytest +from pyanilist import ( + AsyncAnilist, + HTTPStatusError, + MediaType, + ValidationError, +) + + +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) + + +async def test_anilist_bad_search_combo() -> None: + with pytest.raises(HTTPStatusError, match="Not Found."): + await AsyncAnilist().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 + + +async def test_anilist_bad_id() -> None: + with pytest.raises(HTTPStatusError, match="400 Bad Request"): + await AsyncAnilist().get(9999999999) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py new file mode 100644 index 0000000..da1518e --- /dev/null +++ b/tests/test_exceptions.py @@ -0,0 +1,27 @@ +import pytest +from pyanilist import ( + Anilist, + HTTPStatusError, + MediaType, + ValidationError, +) + + +def test_anilist_title_doesnt_exist() -> None: + with pytest.raises(HTTPStatusError, match="Not Found."): + 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) + + +def test_anilist_wrong_input_types() -> None: + with pytest.raises(ValidationError): + 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) From 19446af35cf63e4af9d4cf2d9af5a5635f968a9c Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 19:29:44 +0530 Subject: [PATCH 06/10] Update test.yml --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d2f25dc..6ee5daa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,7 @@ jobs: test: name: Tests strategy: + max-parallel: 3 fail-fast: false matrix: python-version: ['3.9', '3.10', '3.11', '3.12'] From aae70905652f7d918fcb4e1677b4733d164737f5 Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sat, 16 Mar 2024 21:24:10 +0530 Subject: [PATCH 07/10] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d2f25dc..ca286bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ name: Tests -on: [push, pull_request, workflow_dispatch] +on: [pull_request, workflow_dispatch] defaults: run: From a6360c322cc9cd6d74558ad6565f9fe17ba25a0f Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:40:04 +0530 Subject: [PATCH 08/10] fix: make retries --- src/pyanilist/_clients/_async.py | 26 ++++++++++++++++---------- src/pyanilist/_clients/_sync.py | 24 +++++++++++++++--------- tests/test_async_exceptions.py | 10 ++++++---- tests/test_exceptions.py | 10 ++++++---- 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/pyanilist/_clients/_async.py b/src/pyanilist/_clients/_async.py index 44efd92..83f726d 100644 --- a/src/pyanilist/_clients/_async.py +++ b/src/pyanilist/_clients/_async.py @@ -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 @@ -14,7 +14,9 @@ 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. @@ -22,18 +24,16 @@ def __init__(self, api_url: str = "https://graphql.anilist.co", **httpx_async_cl ---------- 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, @@ -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 diff --git a/src/pyanilist/_clients/_sync.py b/src/pyanilist/_clients/_sync.py index e369b75..b07afeb 100644 --- a/src/pyanilist/_clients/_sync.py +++ b/src/pyanilist/_clients/_sync.py @@ -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 @@ -14,7 +14,9 @@ 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. @@ -22,18 +24,16 @@ def __init__(self, api_url: str = "https://graphql.anilist.co", **httpx_client_k ---------- 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, @@ -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 diff --git a/tests/test_async_exceptions.py b/tests/test_async_exceptions.py index c0c6139..14dbb2c 100644 --- a/tests/test_async_exceptions.py +++ b/tests/test_async_exceptions.py @@ -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) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index da1518e..5629ed5 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -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) From 46c00eace891084b4f09de6feef098a6064a7db6 Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:48:25 +0530 Subject: [PATCH 09/10] Update test.yml --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5f44c32..e11c51a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,6 @@ jobs: test: name: Tests strategy: - max-parallel: 3 fail-fast: false matrix: python-version: ['3.9', '3.10', '3.11', '3.12'] @@ -49,10 +48,11 @@ jobs: run: | poetry run pytest - - name: Build - run: poetry build + - name: Get version + id: version + run: echo "version=$(poetry run python -c 'import pyanilist; print(pyanilist.__version__)')" >> $GITHUB_OUTPUT - uses: actions/upload-artifact@v4 with: - name: pyanilist-${{ matrix.python-version }}-${{ matrix.os }} + name: pyanilist-${{ steps.version.outputs.version }}-${{ matrix.python-version }}-${{ matrix.os }} path: "dist/*" From f604f17396433d5ea6088735a4708920eba301de Mon Sep 17 00:00:00 2001 From: Raventric <78981416+Ravencentric@users.noreply.github.com> Date: Sun, 17 Mar 2024 01:54:06 +0530 Subject: [PATCH 10/10] Update test.yml --- .github/workflows/test.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e11c51a..b6b376c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,17 @@ name: Tests -on: [pull_request, workflow_dispatch] +on: + push: + branches: + - main + paths: + - 'src/**' + pull_request: + branches: + - main + paths: + - 'src/**' + workflow_dispatch: defaults: run: @@ -45,8 +56,10 @@ jobs: run: poetry install - name: Run tests - run: | - poetry run pytest + run: poetry run pytest + + - name: Build + run: poetry build - name: Get version id: version