Skip to content

Commit

Permalink
feat: .get() method on enums
Browse files Browse the repository at this point in the history
  • Loading branch information
Ravencentric committed Sep 17, 2024
1 parent 305f86b commit 64a9e2a
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 98 deletions.
4 changes: 2 additions & 2 deletions docs/api-reference/enums.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
::: pynyaa._enums.NyaaCategory
::: pynyaa._enums.Category
options:
members: true
::: pynyaa._enums.NyaaFilter
::: pynyaa._enums.Filter
options:
members: true
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ target-version = "py310"
extend-select = ["I"]
fixable = ["ALL"]

[tool.ruff.lint.isort]
required-imports = ["from __future__ import annotations"]

[tool.mypy]
strict = true
pretty = true
Expand Down
9 changes: 6 additions & 3 deletions src/pynyaa/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

from httpx import HTTPStatusError

from pynyaa._clients._async import AsyncNyaa
from pynyaa._clients._sync import Nyaa
from pynyaa._enums import NyaaCategory, NyaaFilter
from pynyaa._enums import Category, Filter, SortBy
from pynyaa._models import NyaaTorrentPage, Submitter
from pynyaa._version import __version__, __version_tuple__

Expand All @@ -11,8 +13,9 @@
"AsyncNyaa",
"Nyaa",
# Enums
"NyaaCategory",
"NyaaFilter",
"Category",
"Filter",
"SortBy",
# Models
"NyaaTorrentPage",
"Submitter",
Expand Down
3 changes: 3 additions & 0 deletions src/pynyaa/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations


def main() -> None:
"""
Pretty simple CLI implementation.
Expand Down
10 changes: 5 additions & 5 deletions src/pynyaa/_clients/_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from torf import Torrent
from typing_extensions import AsyncGenerator

from pynyaa._enums import NyaaCategory, NyaaFilter
from pynyaa._enums import Category, Filter
from pynyaa._models import NyaaTorrentPage
from pynyaa._parser import parse_nyaa_rss_page, parse_nyaa_torrent_page
from pynyaa._utils import get_user_cache_path
Expand Down Expand Up @@ -102,8 +102,8 @@ async def search(
self,
query: str,
*,
category: NyaaCategory | None = None,
filter: NyaaFilter | None = None,
category: Category | None = None,
filter: Filter | None = None,
) -> AsyncGenerator[NyaaTorrentPage]:
"""
Search for torrents on Nyaa.
Expand All @@ -112,9 +112,9 @@ async def search(
----------
query : str
The search query string.
category : NyaaCategory, optional
category : Category, optional
The category to filter the search. If None, searches all categories.
filter : NyaaFilter, optional
filter : Filter, optional
The filter to apply to the search results. If None, no filter is applied.
Raises
Expand Down
10 changes: 5 additions & 5 deletions src/pynyaa/_clients/_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from torf import Torrent
from typing_extensions import Generator

from pynyaa._enums import NyaaCategory, NyaaFilter
from pynyaa._enums import Category, Filter
from pynyaa._models import NyaaTorrentPage
from pynyaa._parser import parse_nyaa_rss_page, parse_nyaa_torrent_page
from pynyaa._utils import get_user_cache_path
Expand Down Expand Up @@ -99,8 +99,8 @@ def search(
self,
query: str,
*,
category: NyaaCategory | None = None,
filter: NyaaFilter | None = None,
category: Category | None = None,
filter: Filter | None = None,
) -> Generator[NyaaTorrentPage]:
"""
Search for torrents on Nyaa.
Expand All @@ -109,9 +109,9 @@ def search(
----------
query : str
The search query string.
category : NyaaCategory, optional
category : Category, optional
The category to filter the search. If None, searches all categories.
filter : NyaaFilter, optional
filter : Filter, optional
The filter to apply to the search results. If None, no filter is applied.
Raises
Expand Down
2 changes: 2 additions & 0 deletions src/pynyaa/_compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Compatibility module to for older python versions"""

from __future__ import annotations

import sys

if sys.version_info >= (3, 11):
Expand Down
168 changes: 127 additions & 41 deletions src/pynyaa/_enums.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
from ._compat import IntEnum, StrEnum
from __future__ import annotations

from typing import overload

class NyaaCategory(StrEnum):
from typing_extensions import Self

from pynyaa._compat import IntEnum, StrEnum
from pynyaa._types import CategoryID, CategoryName, SortName
from pynyaa._utils import get_category_id_from_name


class BaseStrEnum(StrEnum):
"""StrEnum with case-insensitive lookup"""

@classmethod
def _missing_(cls, value: object) -> Self:
for member in cls:
if member.value.casefold() == str(value).casefold():
return member
message = f"'{value}' is not a valid {type(cls)}"
raise ValueError(message)


class Category(BaseStrEnum):
"""Nyaa categories"""

ALL = "All"

ANIME = "Anime"
ANIME_MUSIC_VIDEO = "Anime - Anime Music Video"
ANIME_ENGLISH_TRANSLATED = "Anime - English-translated"
Expand Down Expand Up @@ -34,51 +56,115 @@ class NyaaCategory(StrEnum):
SOFTWARE_GAMES = "Software - Games"

@property
def id(self) -> str:
def id(self) -> CategoryID:
"""
Returns the ID of the category.
This ID corresponds to the category as seen in the URL
`https://nyaa.si/?f=0&c=1_2&q=`, where `c=1_2` is the ID for `Anime - English-translated`.
"""
mapping = {
# All, c=0_0
"All categories": "0_0",
# Anime, c=1_X
"Anime": "1_0",
"Anime - Anime Music Video": "1_1",
"Anime - English-translated": "1_2",
"Anime - Non-English-translated": "1_3",
"Anime - Raw": "1_4",
# Audio, c=2_X
"Audio": "2_0",
"Audio - Lossless": "2_1",
"Audio - Lossy": "2_2",
# Literature, c=3_X
"Literature": "3_0",
"Literature - English-translated": "3_1",
"Literature - Non-English-translated": "3_2",
"Literature - Raw": "3_3",
# Live Action, c=4_X
"Live Action": "4_0",
"Live Action - English-translated": "4_1",
"Live Action - Idol/Promotional Video": "4_2",
"Live Action - Non-English-translated": "4_3",
"Live Action - Raw": "4_4",
# Pictures, c=5_X
"Pictures": "5_0",
"Pictures - Graphics": "5_1",
"Pictures - Photos": "5_2",
# Software, c=6_X
"Software": "6_0",
"Software - Applications": "6_1",
"Software - Games": "6_2",
}

return mapping.get(self.value, "0_0")


class NyaaFilter(IntEnum):
return get_category_id_from_name(self.value)

@overload
@classmethod
def get(cls, key: CategoryName, default: CategoryName = "All") -> Self: ...

@overload
@classmethod
def get(cls, key: CategoryName, default: str = "All") -> Self: ...

@overload
@classmethod
def get(cls, key: str, default: CategoryName = "All") -> Self: ...

@overload
@classmethod
def get(cls, key: str, default: str = "All") -> Self: ...

@classmethod
def get(cls, key: CategoryName | str, default: CategoryName | str = "All") -> Self:
"""
Get the `Category` by its name (case-insensitive).
Return the default if the key is missing or invalid.
Parameters
----------
key : CategoryName | str
The key to retrieve.
default : CategoryName | str, optional
The default value to return if the key is missing or invalid.
Returns
-------
Category
The `Category` corresponding to the key.
"""
match key:
case str():
for category in cls:
if category.value.casefold() == key.casefold():
return category
else:
return cls(default)
case _:
return cls(default)


class SortBy(BaseStrEnum):
COMMENTS = "comments"
SIZE = "size"
DATETIME = "id" # yea... https://nyaa.si/?s=id&o=desc
SEEDERS = "seeders"
LEECHERS = "leechers"
DOWNLOADS = "downloads"

@overload
@classmethod
def get(cls, key: SortName, default: SortName = "datetime") -> Self: ...

@overload
@classmethod
def get(cls, key: SortName, default: str = "datetime") -> Self: ...

@overload
@classmethod
def get(cls, key: str, default: SortName = "datetime") -> Self: ...

@overload
@classmethod
def get(cls, key: str, default: str = "datetime") -> Self: ...

@classmethod
def get(cls, key: SortName | str, default: SortName | str = "datetime") -> Self:
"""
Get the `SortBy` by its name (case-insensitive).
Return the default if the key is missing or invalid.
Parameters
----------
key : SortName | str
The key to retrieve.
default : SortName | str, optional
The default value to return if the key is missing or invalid.
Returns
-------
Category
The `SortBy` corresponding to the key.
"""
default = "id" if default.casefold() == "datetime" else default.casefold()
key = key.casefold()

match key:
case "comments" | "size" | "id" | "seeders" | "leechers" | "downloads":
return cls(key)
case "datetime":
return cls("id")
case _:
return cls(default)


class Filter(IntEnum):
"""Nyaa search filters"""

NO_FILTER = 0
Expand Down
4 changes: 2 additions & 2 deletions src/pynyaa/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pydantic import BaseModel, ConfigDict, HttpUrl, field_serializer, field_validator
from torf import Torrent

from ._enums import NyaaCategory
from ._enums import Category
from ._types import MagnetUrl


Expand Down Expand Up @@ -145,7 +145,7 @@ class NyaaTorrentPage(ParentModel):
title: str
"""Title of the torrent."""

category: NyaaCategory
category: Category
"""Torrent category."""

date: datetime
Expand Down
61 changes: 60 additions & 1 deletion src/pynyaa/_types.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,64 @@
from __future__ import annotations

from typing import Annotated, Literal

from pydantic import AnyUrl, UrlConstraints
from typing_extensions import Annotated

MagnetUrl = Annotated[AnyUrl, UrlConstraints(allowed_schemes=["magnet"])]
"""Url that only allows magnets."""

CategoryName = Literal[
"All",
"Anime",
"Anime - Anime Music Video",
"Anime - English-translated",
"Anime - Non-English-translated",
"Anime - Raw",
"Audio",
"Audio - Lossless",
"Audio - Lossy",
"Literature",
"Literature - English-translated",
"Literature - Non-English-translated",
"Literature - Raw",
"Live Action",
"Live Action - English-translated",
"Live Action - Idol/Promotional Video",
"Live Action - Non-English-translated",
"Live Action - Raw",
"Pictures",
"Pictures - Graphics",
"Pictures - Photos",
"Software",
"Software - Applications",
"Software - Games",
]

CategoryID = Literal[
"0_0",
"1_0",
"1_1",
"1_2",
"1_3",
"1_4",
"2_0",
"2_1",
"2_2",
"3_0",
"3_1",
"3_2",
"3_3",
"4_0",
"4_1",
"4_2",
"4_3",
"4_4",
"5_0",
"5_1",
"5_2",
"6_0",
"6_1",
"6_2",
]

SortName = Literal["comments", "size", "id", "datetime", "seeders", "leechers", "downloads"]
Loading

0 comments on commit 64a9e2a

Please sign in to comment.