diff --git a/README.md b/README.md index 8f98d25..acd260c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ * [About](#about) * [Installation](#installation) -* [API Reference](#api-reference) +* [Docs](#docs) * [License](#license) ## About @@ -47,73 +47,95 @@ pip install pyanilist ## Usage -PyAnilist offers two main classes: - 1. `Anilist()` - Synchronous class - `search()` - Search a media ```py - >>> from pyanilist import Anilist, MediaType - >>> media = Anilist().search("Attack on Titan", type=MediaType.ANIME) - >>> media.title - MediaTitle(romaji='Shingeki no Kyojin', english='Attack on Titan', native='進撃の巨人') - >>> media.title.romaji - 'Shingeki no Kyojin' - >>> media.site_url - Url('https://anilist.co/anime/16498') - >>> media.episodes + from pyanilist import Anilist, MediaType + + media = Anilist().search("Attack on Titan", type=MediaType.ANIME) + + print(media.title.romaji) + """ + Shingeki no Kyojin + """ + print(media.site_url) + """ + https://anilist.co/anime/16498 + """ + print(media.episodes) + """ 25 + """ ``` - `get()` - Get a media by it's Anilist ID ```py - >>> from pyanilist import Anilist - >>> media = Anilist().get(21459) - >>> media.title - MediaTitle(romaji='Boku no Hero Academia', english='My Hero Academia', native='僕のヒーローアカデミア') - >>> media.title.english - 'My Hero Academia' - >>> media.site_url - Url('https://anilist.co/anime/21459') - >>> media.episodes + from pyanilist import Anilist + + media = Anilist().get(21459) + + print(media.title.english) + """ + My Hero Academia + """ + print(media.site_url) + """ + https://anilist.co/anime/21459 + """ + print(media.episodes) + """ 13 + """ ``` 2. `AsyncAnilist()` - Asynchronous class - `search()` - Search a media ```py - >>> import asyncio - >>> from pyanilist import AsyncAnilist, MediaType - >>> media = asyncio.run(AsyncAnilist().search("Attack on Titan", type=MediaType.ANIME)) - >>> media.title - MediaTitle(romaji='Shingeki no Kyojin', english='Attack on Titan', native='進撃の巨人') - >>> media.title.romaji - 'Shingeki no Kyojin' - >>> media.site_url - Url('https://anilist.co/anime/16498') - >>> media.episodes + import asyncio + from pyanilist import AsyncAnilist, MediaType + + media = asyncio.run(AsyncAnilist().search("Attack on Titan", type=MediaType.ANIME)) + + print(media.title.romaji) + """ + Shingeki no Kyojin + """ + print(media.site_url) + """ + https://anilist.co/anime/16498 + """ + print(media.episodes) + """ 25 + """ ``` - `get()` - Get a media by it's Anilist ID ```py - >>> import asyncio - >>> from pyanilist import AsyncAnilist - >>> media = asyncio.run(AsyncAnilist().get(21459)) - >>> media.title - MediaTitle(romaji='Boku no Hero Academia', english='My Hero Academia', native='僕のヒーローアカデミア') - >>> media.title.english - 'My Hero Academia' - >>> media.site_url - Url('https://anilist.co/anime/21459') - >>> media.episodes + import asyncio + from pyanilist import AsyncAnilist + + media = asyncio.run(AsyncAnilist().get(21459)) + + print(media.title.english) + """ + My Hero Academia + """ + print(media.site_url) + """ + https://anilist.co/anime/21459 + """ + print(media.episodes) + """ 13 + """ ``` -## API Reference +## Docs -Checkout the complete API reference [here](https://ravencentric.github.io/pyanilist/). +Checkout the complete documentation [here](https://pyanilist.zip). ## License diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..234dcdf --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,125 @@ +# Examples + +!!! note + These examples use `pyanilist.Anilist` but you can do the same thing with `pyanilist.AsyncAnilist` since they share the same methods + +## Anilist ID + +```py +from pyanilist import Anilist + +media = Anilist().get(16498) + +print(media.title.english) +""" +Attack on Titan +""" +``` + +## Search + +```py +from pyanilist import Anilist + +media = Anilist().search("Attack on titan") + +print(media.format) +""" +ANIME +""" +print(media.title.romaji) +""" +Shingeki no Kyojin +""" +print(media.episodes) +""" +25 +""" +``` + +## Search with constraints + +```py +from pyanilist import Anilist, MediaSeason, MediaType, MediaStatus, MediaFormat +media = Anilist().search( + "My Hero Academia", + season=MediaSeason.SPRING, + season_year=2016, + type=MediaType.ANIME, + format=MediaFormat.TV, + status=MediaStatus.FINISHED, + +) +print(media.title.romaji) +""" +Boku no Hero Academia +""" +print(media.start_date.iso_format()) +""" +2016-04-03 +""" +print(media.site_url) +""" +https://anilist.co/anime/21459 +""" +``` + +## Related media + +```py +from pyanilist import Anilist + +media = Anilist().search("violet evergarden") + +print(media.format) +""" +TV +""" + +for relation in media.relations: + print(f"{relation.title.romaji} ({relation.format}) - {relation.site_url}") +""" +Violet Evergarden: Kitto "Ai" wo Shiru Hi ga Kuru no Darou (OVA) - https://anilist.co/anime/101432 +Violet Evergarden (NOVEL) - https://anilist.co/manga/97298 +Violet Evergarden Gaiden: Eien to Jidou Shuki Ningyou (MOVIE) - https://anilist.co/anime/109190 +Violet Evergarden CM (ONA) - https://anilist.co/anime/154164 +""" +``` + +## Retries + +Anilist API is flaky, sometimes it might return an error for a perfectly valid request. `pyanilist` handles this by simply retrying failed requests a specified number of times (default is 5) before raising an error. Every subsequent retry also adds an additional one-second delay between requests. + +```py +from pyanilist import Anilist + +# Configure the number of retries. Setting it to 1 basically disables retrying. +anilist = Anilist(retries=1) + +media = anilist.search("violet evergarden") + +print(f"{media.title.english} - {media.site_url}") +""" +Violet Evergarden - https://anilist.co/anime/21827 +""" +``` + +## Client + +`pyanilist` gives you direct access to the internal [`httpx.Client()`](https://www.python-httpx.org/api/#client) used to send the POST request. + +```py +from pyanilist import Anilist + +headers = {'user-agent': 'my-app/0.0.1'} + +# You can pass any httpx.Client() keyword argument to Anilist() +anilist = Anilist(headers=headers) + +media = anilist.get(105333) + +print(media.title.english) +""" +Dr. STONE +""" +``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 7285e68..6df01d2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,17 +10,15 @@

-
- -[![PyPI - Version](https://img.shields.io/pypi/v/pyanilist?link=https%3A%2F%2Fpypi.org%2Fproject%2Fpyanilist%2F)](https://pypi.org/project/pyanilist/) -![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyanilist) -![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/Ravencentric/pyanilist/release.yml) -![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ravencentric/pyanilist/test.yml?label=tests&link=https%3A%2F%2Fgithub.com%2FRavencentric%2Fpyanilist%2Factions%2Fworkflows%2Ftest.yml) -![License](https://img.shields.io/github/license/Ravencentric/pyanilist) -![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg) -![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json) - -
+

+PyPI - Version +PyPI - Python Version +GitHub Workflow Status +GitHub Workflow Status +License +Checked with mypy +Ruff +

## About @@ -45,68 +43,88 @@ PyAnilist offers two main classes: - `search()` - Search a media ```py - >>> from pyanilist import Anilist, MediaType - >>> media = Anilist().search("Attack on Titan", type=MediaType.ANIME) - >>> media.title - MediaTitle(romaji='Shingeki no Kyojin', english='Attack on Titan', native='進撃の巨人') - >>> media.title.romaji - 'Shingeki no Kyojin' - >>> media.site_url - Url('https://anilist.co/anime/16498') - >>> media.episodes + from pyanilist import Anilist, MediaType + + media = Anilist().search("Attack on Titan", type=MediaType.ANIME) + + print(media.title.romaji) + """ + Shingeki no Kyojin + """ + print(media.site_url) + """ + https://anilist.co/anime/16498 + """ + print(media.episodes) + """ 25 + """ ``` - `get()` - Get a media by it's Anilist ID ```py - >>> from pyanilist import Anilist - >>> media = Anilist().get(21459) - >>> media.title - MediaTitle(romaji='Boku no Hero Academia', english='My Hero Academia', native='僕のヒーローアカデミア') - >>> media.title.english - 'My Hero Academia' - >>> media.site_url - Url('https://anilist.co/anime/21459') - >>> media.episodes + from pyanilist import Anilist + + media = Anilist().get(21459) + + print(media.title.english) + """ + My Hero Academia + """ + print(media.site_url) + """ + https://anilist.co/anime/21459 + """ + print(media.episodes) + """ 13 + """ ``` 2. `AsyncAnilist()` - Asynchronous class - `search()` - Search a media ```py - >>> import asyncio - >>> from pyanilist import AsyncAnilist, MediaType - >>> media = asyncio.run(AsyncAnilist().search("Attack on Titan", type=MediaType.ANIME)) - >>> media.title - MediaTitle(romaji='Shingeki no Kyojin', english='Attack on Titan', native='進撃の巨人') - >>> media.title.romaji - 'Shingeki no Kyojin' - >>> media.site_url - Url('https://anilist.co/anime/16498') - >>> media.episodes + import asyncio + from pyanilist import AsyncAnilist, MediaType + + media = asyncio.run(AsyncAnilist().search("Attack on Titan", type=MediaType.ANIME)) + + print(media.title.romaji) + """ + Shingeki no Kyojin + """ + print(media.site_url) + """ + https://anilist.co/anime/16498 + """ + print(media.episodes) + """ 25 + """ ``` - `get()` - Get a media by it's Anilist ID ```py - >>> import asyncio - >>> from pyanilist import AsyncAnilist - >>> media = asyncio.run(AsyncAnilist().get(21459)) - >>> media.title - MediaTitle(romaji='Boku no Hero Academia', english='My Hero Academia', native='僕のヒーローアカデミア') - >>> media.title.english - 'My Hero Academia' - >>> media.site_url - Url('https://anilist.co/anime/21459') - >>> media.episodes + import asyncio + from pyanilist import AsyncAnilist + + media = asyncio.run(AsyncAnilist().get(21459)) + + print(media.title.english) + """ + My Hero Academia + """ + print(media.site_url) + """ + https://anilist.co/anime/21459 + """ + print(media.episodes) + """ 13 + """ ``` -## API Reference - -Checkout the complete API reference [here](https://ravencentric.github.io/pyanilist/). - ## License Distributed under the [Unlicense](https://choosealicense.com/licenses/unlicense/) License. See [UNLICENSE](https://github.com/Ravencentric/pyanilist/blob/main/UNLICENSE) for more information. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 04b25ea..b38604a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,6 +78,7 @@ markdown_extensions: nav: - Home: index.md + - Examples: examples.md - API Reference: - Clients: api-reference/clients.md - Models: api-reference/models.md diff --git a/src/pyanilist/_clients/_async.py b/src/pyanilist/_clients/_async.py index 83f726d..595aa06 100644 --- a/src/pyanilist/_clients/_async.py +++ b/src/pyanilist/_clients/_async.py @@ -178,7 +178,7 @@ async def search( Parameters ---------- title : AnilistTitle | None, optional - The string used for searching on Anilist. Default is None. + The string used for searching on Anilist. season : MediaSeason | None, optional The season the media was initially released in. Default is None. season_year : AnilistYear | None, optional @@ -194,8 +194,8 @@ async def search( ------ ValidationError Invalid input - pyanilist.exceptions.* - Any exception from the pyanilist.exceptions module may be raised + pyanilist._exceptions.* + Any exception from the pyanilist._exceptions module may be raised in case of errors encountered during the POST request. Returns @@ -231,8 +231,8 @@ async def get(self, id: AnilistID) -> Media: ------ ValidationError Invalid input - pyanilist.exceptions.* - Any exception from the pyanilist.exceptions module may be raised + pyanilist._exceptions.* + Any exception from the pyanilist._exceptions module may be raised in case of errors encountered during the POST request. Returns diff --git a/src/pyanilist/_clients/_sync.py b/src/pyanilist/_clients/_sync.py index b07afeb..34d5f8c 100644 --- a/src/pyanilist/_clients/_sync.py +++ b/src/pyanilist/_clients/_sync.py @@ -193,8 +193,8 @@ def search( ------ ValidationError Invalid input - pyanilist.exceptions.* - Any exception from the pyanilist.exceptions module may be raised + pyanilist._exceptions.* + Any exception from the pyanilist._exceptions module may be raised in case of errors encountered during the POST request. Returns @@ -230,8 +230,8 @@ def get(self, id: AnilistID) -> Media: ------ ValidationError Invalid input - pyanilist.exceptions.* - Any exception from the pyanilist.exceptions module may be raised + pyanilist._exceptions.* + Any exception from the pyanilist._exceptions module may be raised in case of errors encountered during the POST request. Returns