diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 319ce51..138ce97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,19 +21,11 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Install Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install PDM + - name: Setup Python ${{ matrix.python-version }} & PDM uses: pdm-project/setup-pdm@v4 - - - name: Cache the Virtual Env - uses: actions/cache@v4 with: - path: ./.venv - key: venv-${{ matrix.python-version }}-${{ hashFiles('pdm.lock') }} + python-version: ${{ matrix.python-version }} + cache: true - name: Install the project dependencies run: pdm install -d diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ffd1ae..5ed25f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,9 @@ on: tags: - "*" +env: + PYTHON_VERSION: 3.13 + jobs: release: runs-on: ubuntu-latest @@ -18,13 +21,10 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Install Python - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install PDM + - name: Setup Python & PDM uses: pdm-project/setup-pdm@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} - name: Install the project dependencies run: pdm install -d diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..d019b34 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,33 @@ +name: Run tests +on: + push: + branches: + - master + tags: + - v* + pull_request: + branches: + - master + +env: + PYTHON_VERSION: 3.13 + +jobs: + lint: + name: Validate Linter + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Python & PDM + uses: pdm-project/setup-pdm@v4 + with: + python-version: ${{ env.PYTHON_VERSION }} + cache: true + + - name: Install the project dependencies + run: pdm install -d + + - name: Lint the project + run: pdm check diff --git a/pyproject.toml b/pyproject.toml index 3494fa9..eef6e1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,10 +70,13 @@ path = "src/tcgdexsdk/__init__.py" # Ruff # ######## [tool.ruff] -line-length = 80 +line-length = 120 respect-gitignore = true include = ["src/**/*.py"] +[tool.ruff.lint.pycodestyle] +ignore-overlong-task-comments = true + [tool.ruff.lint] select = ["E", "F", "UP", "B", "SIM", "I"] diff --git a/src/tcgdexsdk/__init__.py b/src/tcgdexsdk/__init__.py index 08cfb13..2f78d6c 100644 --- a/src/tcgdexsdk/__init__.py +++ b/src/tcgdexsdk/__init__.py @@ -10,6 +10,7 @@ from tcgdexsdk.models.Set import Set from tcgdexsdk.models.SetResume import SetResume from tcgdexsdk.models.StringEndpoint import StringEndpoint +from tcgdexsdk.query import Query from tcgdexsdk.tcgdex import TCGdex __all__ = [ @@ -23,4 +24,5 @@ "Set", "SetResume", "StringEndpoint", + "Query", ] diff --git a/src/tcgdexsdk/endpoints/Endpoint.py b/src/tcgdexsdk/endpoints/Endpoint.py index dea377a..1b93098 100644 --- a/src/tcgdexsdk/endpoints/Endpoint.py +++ b/src/tcgdexsdk/endpoints/Endpoint.py @@ -1,8 +1,8 @@ from typing import Generic, List, Optional, Type, TypeVar, Union from tcgdexsdk.models.Model import Model -from tcgdexsdk.utils import fetch, fetch_list from tcgdexsdk.query import Query +from tcgdexsdk.utils import fetch, fetch_list # Generic type variables Item = TypeVar('Item', bound=Model) @@ -20,7 +20,11 @@ def __init__(self, self.endpoint = endpoint async def get(self, id: str) -> Optional[Item]: - return fetch(self.tcgdex, f"{self.tcgdex.getEndpoint()}/{self.tcgdex.language}/{self.endpoint}/{id.replace(' ', '%20')}", self.item_model) + return fetch( + self.tcgdex, + f"{self.tcgdex.getEndpoint()}/{self.tcgdex.language}/{self.endpoint}/{id.replace(' ', '%20')}", + self.item_model + ) async def list(self, query: Optional[Query] = None) -> List[ListModel]: url = f"{self.tcgdex.getEndpoint()}/{self.tcgdex.language}/{self.endpoint}" @@ -29,7 +33,11 @@ async def list(self, query: Optional[Query] = None) -> List[ListModel]: return fetch_list(self.tcgdex, url, self.list_model) def getSync(self, id: str) -> Optional[Item]: - return fetch(self.tcgdex, f"{self.tcgdex.getEndpoint()}/{self.tcgdex.language}/{self.endpoint}/{id.replace(' ', '%20')}", self.item_model) + return fetch( + self.tcgdex, + f"{self.tcgdex.getEndpoint()}/{self.tcgdex.language}/{self.endpoint}/{id.replace(' ', '%20')}", + self.item_model + ) def listSync(self, query: Optional[Query] = None) -> List[ListModel]: url = f"{self.tcgdex.getEndpoint()}/{self.tcgdex.language}/{self.endpoint}" diff --git a/src/tcgdexsdk/query.py b/src/tcgdexsdk/query.py index 668fe38..d324ecf 100644 --- a/src/tcgdexsdk/query.py +++ b/src/tcgdexsdk/query.py @@ -1,5 +1,6 @@ from urllib.parse import quote + class Query: def __init__(self): self.params = [] @@ -12,7 +13,9 @@ def encode(self, value): ) def build(self): - return '?' + '&'.join(f"{self.encode(item['key'])}={self.encode(item['value'])}" for item in self.params) + return '?' + '&'.join( + f"{self.encode(item['key'])}={self.encode(item['value'])}" for item in self.params + ) def includes(self, key: str, value: str): return self.contains(key, value) @@ -67,7 +70,10 @@ def isNull(self, key: str): def paginate(self, page: int, itemsPerPage: int): self.params.append({'key': 'pagination:page', 'value': page}) - self.params.append({'key': 'pagination:itemsPerPage', 'value': itemsPerPage}) + self.params.append({ + 'key': 'pagination:itemsPerPage', + 'value': itemsPerPage + }) return self def notEqual(self, key: str, value: str): diff --git a/src/tcgdexsdk/tcgdex.py b/src/tcgdexsdk/tcgdex.py index dfde8c9..927c441 100644 --- a/src/tcgdexsdk/tcgdex.py +++ b/src/tcgdexsdk/tcgdex.py @@ -1,5 +1,4 @@ from typing import Union -from warnings import deprecated from tcgdexsdk.endpoints.Endpoint import Endpoint from tcgdexsdk.enums import Language @@ -10,6 +9,7 @@ from tcgdexsdk.models.Set import Set from tcgdexsdk.models.SetResume import SetResume from tcgdexsdk.models.StringEndpoint import StringEndpoint +from tcgdexsdk.utils import deprecated class TCGdex: @@ -28,7 +28,7 @@ def getEndpoint(self) -> str: @deprecated("use (get|set)endpoint instead") def URI(self): """ - @Deprecated: use `getEndpoint()` or `setEndpoint()` instead. + @Deprecated: use `getEndpoint()` or `setEndpoint()` instead. """ return self.getEndpoint() @@ -58,9 +58,7 @@ def __init__(self, language: Union[str, Language] = Language.EN): self.trainerType = Endpoint(self, StringEndpoint, str, "trainer-types") self.suffix = Endpoint(self, StringEndpoint, str, "suffixes") self.stage = Endpoint(self, StringEndpoint, str, "stages") - self.regulationMark = Endpoint( - self, StringEndpoint, str, "regulation-marks" - ) + self.regulationMark = Endpoint(self, StringEndpoint, str, "regulation-marks") self.energyType = Endpoint(self, StringEndpoint, str, "energy-types") self.dexId = Endpoint(self, StringEndpoint, int, "dex-ids") self.type = Endpoint(self, StringEndpoint, str, "types") @@ -69,4 +67,3 @@ def __init__(self, language: Union[str, Language] = Language.EN): self.illustrator = Endpoint(self, StringEndpoint, str, "illustrators") self.hp = Endpoint(self, StringEndpoint, int, "hp") self.category = Endpoint(self, StringEndpoint, str, "categories") - diff --git a/src/tcgdexsdk/utils.py b/src/tcgdexsdk/utils.py index c9ad659..1b20f75 100644 --- a/src/tcgdexsdk/utils.py +++ b/src/tcgdexsdk/utils.py @@ -1,6 +1,7 @@ import json import time -from functools import lru_cache +import warnings +from functools import lru_cache, wraps from http.client import HTTPResponse from typing import List, Optional, Type, TypeVar from urllib.request import Request, urlopen @@ -62,3 +63,17 @@ def fetch_list(tcgdex, url: str, cls: Type[_T]) -> List[_T]: def download_image(url: str) -> HTTPResponse: return urlopen(_request(url)) + + +def deprecated(reason): + def decorator(func): + message = f"{func.__name__} is deprecated: {reason}" + + @wraps(func) + def wrapper(*args, **kwargs): + warnings.warn(message, category=DeprecationWarning, stacklevel=2) + return func(*args, **kwargs) + + return wrapper + + return decorator diff --git a/tests/tests.py b/tests/tests.py index c7bf046..dd7071f 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,7 +1,7 @@ import unittest from typing import Callable from unittest.mock import patch -from warnings import deprecated + import vcr from tcgdexsdk import Language, TCGdex @@ -14,6 +14,7 @@ from tcgdexsdk.models.StringEndpoint import StringEndpoint from tcgdexsdk.query import Query + def _use_cassette(test: Callable) -> Callable: return vcr.use_cassette(f"tests/.fixtures/{test.__name__}.yaml")(test) @@ -21,19 +22,17 @@ def _use_cassette(test: Callable) -> Callable: class APITest(unittest.IsolatedAsyncioTestCase): def setUp(self): self.api = TCGdex(Language.EN) - - - @deprecated("this test is deprecated") + @patch("tcgdexsdk.endpoints.Endpoint.fetch") @patch("tcgdexsdk.endpoints.Endpoint.fetch_list") async def test_uri(self, mock_fetch_list, mock_fetch): api = TCGdex(Language.EN) - + api.URI = "http://localhost:3000/v2" - + await api.card.get("swsh1-136") mock_fetch.assert_called_once_with(api, "http://localhost:3000/v2/en/cards/swsh1-136", Card) - + await api.card.list() mock_fetch_list.assert_called_once_with(api, "http://localhost:3000/v2/en/cards", CardResume) @@ -55,17 +54,17 @@ async def test_endpoint(self, mock_fetch_list, mock_fetch): @patch("tcgdexsdk.endpoints.Endpoint.fetch_list") async def test_language(self, mock_fetch_list, mock_fetch): api = TCGdex() - + # Default language should be english self.assertEqual(api.getLanguage(), Language.EN) - + # Card should be fetched in english await api.card.get("swsh1-136") mock_fetch.assert_called_once_with(api, f"{api.getEndpoint()}/en/cards/swsh1-136", Card) # Card should be fetched in french api.setLanguage(Language.FR) - + # Test that the language is set correctly self.assertEqual(api.getLanguage(), Language.FR) await api.card.get("swsh1-136") @@ -74,25 +73,25 @@ async def test_language(self, mock_fetch_list, mock_fetch): @_use_cassette async def test_fr(self): tcg = TCGdex(Language.FR) - res = await tcg.card.get('swsh3-136') - self.assertEqual(res.name, 'Fouinar') - tcg2 = TCGdex('fr') - res = await tcg2.card.get('swsh3-136') - self.assertEqual(res.name, 'Fouinar') + res = await tcg.card.get("swsh3-136") + self.assertEqual(res.name, "Fouinar") + tcg2 = TCGdex("fr") + res = await tcg2.card.get("swsh3-136") + self.assertEqual(res.name, "Fouinar") @_use_cassette async def test_query_equal(self): tcg = TCGdex(Language.EN) - res = await tcg.card.list(Query().equal('name', 'Furret')) + res = await tcg.card.list(Query().equal("name", "Furret")) for card in res: - self.assertEqual(card.name, 'Furret') - + self.assertEqual(card.name, "Furret") + @_use_cassette async def test_query_not_equal(self): tcg = TCGdex() - res = await tcg.card.list(Query().notEqual('name', 'Furret')) + res = await tcg.card.list(Query().notEqual("name", "Furret")) for card in res: - self.assertNotEqual(card.name, 'Furret') + self.assertNotEqual(card.name, "Furret") @_use_cassette async def test_card_resume(self):