From 3d6ae815f1d3b18fe54a5f516f21350fcc5b02a2 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:00:38 -0400 Subject: [PATCH 1/7] Ajout d'async MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize state correctement avec un dict vide. Ajout des statement async lorsqu'utilisés --- pyhilo/api.py | 24 ++++++++++++------------ pyhilo/util/state.py | 17 ++++++++++------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/pyhilo/api.py b/pyhilo/api.py index 5b9c155..150408b 100644 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -75,7 +75,7 @@ def __init__( self._backoff_refresh_lock_ws = asyncio.Lock() self._request_retries = request_retries self._state_yaml: str = DEFAULT_STATE_FILE - self.state = get_state(self._state_yaml) + self.state = {} self.async_request = self._wrap_request_method(self._request_retries) self.device_attributes = get_device_attributes() self.session: ClientSession = session @@ -152,14 +152,14 @@ def dev_atts( else attribute, ) - def _get_fid_state(self) -> bool: + async def _get_fid_state(self) -> bool: """Looks up the cached state to define the firebase attributes on the API instances. :return: Whether or not we have cached firebase state :rtype: bool """ - self.state = get_state(self._state_yaml) + self.state = await get_state(self._state_yaml) fb_state = self.state.get("firebase", {}) if fb_fid := fb_state.get("fid"): self._fb_fid = fb_fid @@ -171,14 +171,14 @@ def _get_fid_state(self) -> bool: return True return False - def _get_android_state(self) -> bool: + async def _get_android_state(self) -> bool: """Looks up the cached state to define the android device token on the API instances. :return: Whether or not we have cached android state :rtype: bool """ - self.state = get_state(self._state_yaml) + self.state = await get_state(self._state_yaml) android_state = self.state.get("android", {}) if token := android_state.get("token"): self._device_token = token @@ -187,18 +187,18 @@ def _get_android_state(self) -> bool: async def _get_device_token(self) -> None: """Retrieves the android token if it's not cached.""" - if not self._get_android_state(): + if not await self._get_android_state(): await self.android_register() async def _get_fid(self) -> None: """Retrieves the firebase state if it's not cached.""" - if not self._get_fid_state(): + if not await self._get_fid_state(): self._fb_id = "".join( random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(FB_ID_LEN) ) await self.fb_install(self._fb_id) - self._get_fid_state() + await self._get_fid_state() async def _async_request( self, method: str, endpoint: str, host: str = API_HOSTNAME, **kwargs: Any @@ -366,7 +366,7 @@ async def post_devicehub_negociate(self) -> tuple[str, str]: resp = await self.async_request("post", url) ws_url = resp.get("url") ws_token = resp.get("accessToken") - set_state( + await set_state( self._state_yaml, "websocket", { @@ -397,7 +397,7 @@ async def get_websocket_params(self) -> None: "available_transports": transport_dict, "full_ws_url": self.full_ws_url, } - set_state(self._state_yaml, "websocket", websocket_dict) + await set_state(self._state_yaml, "websocket", websocket_dict) async def fb_install(self, fb_id: str) -> None: LOG.debug("Posting firebase install") @@ -422,7 +422,7 @@ async def fb_install(self, fb_id: str) -> None: raise RequestError(err) from err LOG.debug(f"FB Install data: {resp}") auth_token = resp.get("authToken", {}) - set_state( + await set_state( self._state_yaml, "firebase", { @@ -463,7 +463,7 @@ async def android_register(self) -> None: LOG.error(f"Android registration error: {msg}") raise RequestError token = msg.split("=")[-1] - set_state( + await set_state( self._state_yaml, "android", { diff --git a/pyhilo/util/state.py b/pyhilo/util/state.py index f7607a9..3767d58 100644 --- a/pyhilo/util/state.py +++ b/pyhilo/util/state.py @@ -2,6 +2,7 @@ from os.path import isfile from typing import Any, Optional, Type, TypedDict, TypeVar, Union +import aiofiles import ruyaml as yaml from pyhilo.const import LOG @@ -71,7 +72,7 @@ def __get_defaults__(cls: Type[T]) -> dict[str, Any]: return new_dict # type: ignore -def get_state(state_yaml: str) -> StateDict: +async def get_state(state_yaml: str) -> StateDict: """Read in state yaml. :param state_yaml: filename where to read the state :type state_yaml: ``str`` @@ -79,13 +80,14 @@ def get_state(state_yaml: str) -> StateDict: """ if not isfile(state_yaml): return __get_defaults__(StateDict) # type: ignore - with open(state_yaml) as yaml_file: + async with aiofiles.open(state_yaml, mode='r') as yaml_file: LOG.debug("Loading state from yaml") - state_yaml_payload: StateDict = yaml.load(yaml_file, Loader=yaml.Loader) + content = await yaml_file.read() + state_yaml_payload: StateDict = yaml.safe_load(content) return state_yaml_payload -def set_state( +async def set_state( state_yaml: str, key: str, state: Union[ @@ -101,9 +103,10 @@ def set_state( :type state: ``StateDict`` :rtype: ``StateDict`` """ - current_state = get_state(state_yaml) or {} + current_state = await get_state(state_yaml) or {} merged_state: dict[str, Any] = {key: {**current_state.get(key, {}), **state}} # type: ignore new_state: dict[str, Any] = {**current_state, **merged_state} - with open(state_yaml, "w") as yaml_file: + async with aiofiles.open(state_yaml, mode='w') as yaml_file: LOG.debug("Saving state to yaml file") - yaml.dump(new_state, yaml_file, Dumper=yaml.RoundTripDumper) + content = yaml.dump(new_state) + await yaml_file.write(content) From 10edc1f18f6492cf032d8e8f60f8ff82afb0966d Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Fri, 7 Jun 2024 17:04:53 -0400 Subject: [PATCH 2/7] Bump up version --- pyhilo/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyhilo/const.py b/pyhilo/const.py index 56a6beb..f60764a 100644 --- a/pyhilo/const.py +++ b/pyhilo/const.py @@ -8,7 +8,7 @@ LOG: Final = logging.getLogger(__package__) DEFAULT_STATE_FILE: Final = "hilo_state.yaml" REQUEST_RETRY: Final = 9 -PYHILO_VERSION: Final = "2024.04.01" +PYHILO_VERSION: Final = "2024.06.01" # TODO: Find a way to keep previous line in sync with pyproject.toml automatically CONTENT_TYPE_FORM: Final = "application/x-www-form-urlencoded" diff --git a/pyproject.toml b/pyproject.toml index bdd6aaf..4a240a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ exclude = ".venv/.*" [tool.poetry] name = "python-hilo" -version = "2024.4.1" +version = "2024.6.1" description = "A Python3, async interface to the Hilo API" readme = "README.md" authors = ["David Vallee Delisle "] From cacfc7890a52200287c3dfb9ed08b2e60260d56a Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Sat, 8 Jun 2024 20:51:12 -0400 Subject: [PATCH 3/7] Update requirements --- pyproject.toml | 3 ++- requirements.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4a240a9..d40c102 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ disallow_untyped_defs = true follow_imports = "silent" ignore_missing_imports = true no_implicit_optional = true -python_version = "3.9" +python_version = "3.11" show_error_codes = true strict_equality = true warn_incomplete_stub = true @@ -65,6 +65,7 @@ classifiers = [ [tool.poetry.dependencies] aiohttp = ">=3.8.0" +aiofiles = ">=23.2.1" aiosignal = ">=1.2.0" async-timeout = ">=4.0.0" attrs = ">=21.2.0" diff --git a/requirements.txt b/requirements.txt index 9b21f4a..98cd29b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +aiofiles >=23.2.1 aiohttp>=3.8.0 aiosignal>=1.1.0 async-timeout>=4.0.0 From 8b5837edf8d67a859b0df29e19f1453bf171ec58 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:05:07 -0400 Subject: [PATCH 4/7] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d40c102..5416cbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ python-dateutil = ">=2.8.2" ruyaml = ">=0.91.0" python = "^3.9.0" voluptuous = ">=0.13.1" -websockets = ">=8.1,<12.0" +websockets = ">=8.1,<13.0" [tool.poetry.dev-dependencies] Sphinx = "^7.1.2" From b5bda3e901e0ead1278ae91cd845ec8de3be4b5e Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Sun, 9 Jun 2024 07:16:05 -0400 Subject: [PATCH 5/7] Adding type hints + some linting --- pyhilo/api.py | 4 ++-- pyhilo/util/state.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyhilo/api.py b/pyhilo/api.py index 150408b..451d8c6 100644 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -6,7 +6,7 @@ import random import string import sys -from typing import Any, Callable, Union, cast +from typing import Any, Callable, Dict, Union, cast from urllib import parse from aiohttp import ClientSession @@ -75,7 +75,7 @@ def __init__( self._backoff_refresh_lock_ws = asyncio.Lock() self._request_retries = request_retries self._state_yaml: str = DEFAULT_STATE_FILE - self.state = {} + self.state: Dict[str, Any] = {} self.async_request = self._wrap_request_method(self._request_retries) self.device_attributes = get_device_attributes() self.session: ClientSession = session diff --git a/pyhilo/util/state.py b/pyhilo/util/state.py index 3767d58..98bc808 100644 --- a/pyhilo/util/state.py +++ b/pyhilo/util/state.py @@ -80,7 +80,7 @@ async def get_state(state_yaml: str) -> StateDict: """ if not isfile(state_yaml): return __get_defaults__(StateDict) # type: ignore - async with aiofiles.open(state_yaml, mode='r') as yaml_file: + async with aiofiles.open(state_yaml, mode="r") as yaml_file: LOG.debug("Loading state from yaml") content = await yaml_file.read() state_yaml_payload: StateDict = yaml.safe_load(content) @@ -106,7 +106,7 @@ async def set_state( current_state = await get_state(state_yaml) or {} merged_state: dict[str, Any] = {key: {**current_state.get(key, {}), **state}} # type: ignore new_state: dict[str, Any] = {**current_state, **merged_state} - async with aiofiles.open(state_yaml, mode='w') as yaml_file: + async with aiofiles.open(state_yaml, mode="w") as yaml_file: LOG.debug("Saving state to yaml file") content = yaml.dump(new_state) await yaml_file.write(content) From 0ab8749a2441c6ff4ba3452082dd18d4a8ef0710 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:43:40 -0400 Subject: [PATCH 6/7] Update .pre-commit-config.yaml Permet de typer les aiofiles --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a968d6..eb18f3f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,6 +49,7 @@ repos: files: ^pyhilo/.+\.py$ additional_dependencies: - types-python-dateutil==2.8.0 + - types-aiofiles==23.2.0 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 From b3c365f19c6e8cfdd58f96c7bfdf421026045e31 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Tue, 11 Jun 2024 19:50:59 -0400 Subject: [PATCH 7/7] Update api.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dernière petite modif de linting --- pyhilo/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyhilo/api.py b/pyhilo/api.py index 451d8c6..85180a8 100644 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -6,7 +6,7 @@ import random import string import sys -from typing import Any, Callable, Dict, Union, cast +from typing import Any, Callable, Union, cast from urllib import parse from aiohttp import ClientSession @@ -45,6 +45,7 @@ from pyhilo.device import DeviceAttribute, HiloDevice, get_device_attributes from pyhilo.exceptions import InvalidCredentialsError, RequestError from pyhilo.util.state import ( + StateDict, WebsocketDict, WebsocketTransportsDict, get_state, @@ -75,7 +76,7 @@ def __init__( self._backoff_refresh_lock_ws = asyncio.Lock() self._request_retries = request_retries self._state_yaml: str = DEFAULT_STATE_FILE - self.state: Dict[str, Any] = {} + self.state: StateDict = {} self.async_request = self._wrap_request_method(self._request_retries) self.device_attributes = get_device_attributes() self.session: ClientSession = session