From ca4f89c33df060403d012fadffeaaa5d8a8fa0a0 Mon Sep 17 00:00:00 2001 From: "Mr. Bubbles" Date: Sat, 22 Jun 2024 03:28:10 +0200 Subject: [PATCH] Refactor type handling and response classes (#35) Change response classes `StatusServerResponse` and `LoginResponse` from dataclass to TypedDict --- src/pyloadapi/api.py | 14 +++++---- src/pyloadapi/types.py | 67 ++---------------------------------------- tests/conftest.py | 6 ++-- tests/test_api.py | 21 ++----------- 4 files changed, 17 insertions(+), 91 deletions(-) diff --git a/src/pyloadapi/api.py b/src/pyloadapi/api.py index cd9afc9..b1b3a5a 100644 --- a/src/pyloadapi/api.py +++ b/src/pyloadapi/api.py @@ -76,10 +76,7 @@ async def login(self) -> LoginResponse: r.raise_for_status() try: - data = await r.json() - if not data: - raise InvalidAuth - return LoginResponse.from_dict(data) + data: LoginResponse = await r.json() except (JSONDecodeError, TypeError, aiohttp.ContentTypeError) as e: _LOGGER.debug( "Exception: Cannot parse login response:\n %s", @@ -88,6 +85,10 @@ async def login(self) -> LoginResponse: raise ParserError( "Login failed during parsing of request response." ) from e + else: + if not data: + raise InvalidAuth + return data except (TimeoutError, aiohttp.ClientError) as e: _LOGGER.debug("Exception: Cannot login:\n %s", traceback.format_exc()) raise CannotConnect from e @@ -199,10 +200,11 @@ async def get_status(self) -> StatusServerResponse: """ try: - r = await self.get(PyLoadCommand.STATUS) - return StatusServerResponse.from_dict(r) + data: StatusServerResponse = await self.get(PyLoadCommand.STATUS) except CannotConnect as e: raise CannotConnect("Get status failed due to request exception") from e + else: + return data async def pause(self) -> None: """Pause the download queue in pyLoad. diff --git a/src/pyloadapi/types.py b/src/pyloadapi/types.py index 722696f..387b19a 100644 --- a/src/pyloadapi/types.py +++ b/src/pyloadapi/types.py @@ -23,73 +23,13 @@ """ -from dataclasses import asdict, dataclass from enum import StrEnum -from typing import Any, TypeVar +from typing import Any, TypedDict, TypeVar T = TypeVar("T") -@dataclass -class Response: - """Base Response class. - - Methods - ------- - from_dict(cls, d) - Class method that converts a dictionary to an instance of the Response class. - to_dict(self) - Converts the instance of the Response class to a dictionary. - - """ - - @classmethod - def from_dict(cls: type[T], d: dict[Any, Any]) -> T: - """Create an instance of the Response class from a dictionary. - - Parameters - ---------- - d : dict - The dictionary to convert. - - Returns - ------- - T - An instance of the Response class. - - """ - return cls(**d) - - def to_dict(self) -> dict[str, Any] | Any: - """Convert the Response instance to a dictionary. - - Returns - ------- - dict - A dictionary representation of the Response instance. - - """ - return asdict(self) - - def __getitem__(self, key: str) -> Any: - """Get the value associated with the given key. - - Parameters - ---------- - key : str - The key for which the value is to be returned. - - Returns - ------- - Any - The value associated with the specified key. - - """ - return getattr(self, key) - - -@dataclass -class StatusServerResponse(Response): +class StatusServerResponse(TypedDict): """Dataclass for status server response. Attributes @@ -124,8 +64,7 @@ class StatusServerResponse(Response): captcha: bool -@dataclass -class LoginResponse(Response): +class LoginResponse(TypedDict): """Dataclass for login response. Attributes diff --git a/tests/conftest.py b/tests/conftest.py index 9307884..94a89dc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ from dotenv import load_dotenv import pytest -from pyloadapi.api import PyLoadAPI +from pyloadapi import PyLoadAPI, StatusServerResponse load_dotenv() @@ -27,12 +27,12 @@ "_flashes": [["message", "Logged in successfully"]], } -TEST_STATUS_RESPONSE = { +TEST_STATUS_RESPONSE: StatusServerResponse = { "pause": False, "active": 10, "queue": 5, "total": 15, - "speed": 9999999, + "speed": 9999999.0, "download": True, "reconnect": False, "captcha": False, diff --git a/tests/test_api.py b/tests/test_api.py index 31e299d..5377d8c 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -11,14 +11,7 @@ from dotenv import load_dotenv import pytest -from pyloadapi import ( - CannotConnect, - InvalidAuth, - ParserError, - PyLoadAPI, - StatusServerResponse, -) -from pyloadapi.types import LoginResponse +from pyloadapi import CannotConnect, InvalidAuth, ParserError, PyLoadAPI from .conftest import TEST_API_URL, TEST_LOGIN_RESPONSE, TEST_STATUS_RESPONSE @@ -32,7 +25,7 @@ async def test_login(pyload: PyLoadAPI, mocked_aiohttp: aioresponses) -> None: ) result = await pyload.login() - assert result.to_dict() == TEST_LOGIN_RESPONSE + assert result == TEST_LOGIN_RESPONSE async def test_login_invalidauth( @@ -49,7 +42,7 @@ async def test_login_invalidauth( ("method", "result"), [ ("version", "0.5.0"), - ("get_status", StatusServerResponse.from_dict(TEST_STATUS_RESPONSE)), + ("get_status", TEST_STATUS_RESPONSE), ("pause", None), ("unpause", None), ("toggle_pause", None), @@ -80,14 +73,6 @@ async def test_api_methods( assert await getattr(pyload, method)() == result -def test_dataclass() -> None: - """Test methods of Response dataclass.""" - result = LoginResponse.from_dict(TEST_LOGIN_RESPONSE) - - assert result.to_dict() == TEST_LOGIN_RESPONSE - assert result["name"] == "test-username" - - @pytest.mark.parametrize( "method", [