Skip to content

Commit

Permalink
Refactor type handling and response classes (#35)
Browse files Browse the repository at this point in the history
Change response classes `StatusServerResponse` and `LoginResponse` from dataclass to TypedDict
  • Loading branch information
tr4nt0r authored Jun 22, 2024
1 parent 81bf1ed commit ca4f89c
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 91 deletions.
14 changes: 8 additions & 6 deletions src/pyloadapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
67 changes: 3 additions & 64 deletions src/pyloadapi/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -124,8 +64,7 @@ class StatusServerResponse(Response):
captcha: bool


@dataclass
class LoginResponse(Response):
class LoginResponse(TypedDict):
"""Dataclass for login response.
Attributes
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dotenv import load_dotenv
import pytest

from pyloadapi.api import PyLoadAPI
from pyloadapi import PyLoadAPI, StatusServerResponse

load_dotenv()

Expand All @@ -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,
Expand Down
21 changes: 3 additions & 18 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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(
Expand All @@ -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),
Expand Down Expand Up @@ -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",
[
Expand Down

0 comments on commit ca4f89c

Please sign in to comment.