From 6245395e15e0d6f2a79ae47b6677335fb91ee97d Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Dec 2022 17:28:13 +0100 Subject: [PATCH 1/6] Add request param to disable trying to decode all responses to text. --- homeassistant_api/processing.py | 11 ++++++++--- homeassistant_api/rawclient.py | 7 ++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/homeassistant_api/processing.py b/homeassistant_api/processing.py index 8ac82808..b9b5fd1d 100644 --- a/homeassistant_api/processing.py +++ b/homeassistant_api/processing.py @@ -37,8 +37,9 @@ class Processing: _response: AllResponseType _processors: ClassVar[Dict[str, Tuple[ProcessorType, ...]]] = {} - def __init__(self, response: AllResponseType) -> None: + def __init__(self, response: AllResponseType, decode_bytes: bool = False) -> None: self._response = response + self._decode_bytes = decode_bytes @staticmethod def processor(mimetype: str) -> Callable[[ProcessorType], ProcessorType]: @@ -75,10 +76,14 @@ def process(self) -> Any: if async_ := isinstance(self._response, (ClientResponse, AsyncCachedResponse)): status_code = self._response.status _buffer = self._response.content._buffer - content = "" if not _buffer else _buffer[0].decode() + content = b"" if not _buffer else _buffer[0].decode() + if self._decode_bytes: + content = content.decode() elif isinstance(self._response, (Response, CachedResponse)): status_code = self._response.status_code - content = self._response.content.decode() + content = self._response.content + if self._decode_bytes: + content = content.decode() if status_code in (200, 201): return self.process_content(async_=async_) if status_code == 400: diff --git a/homeassistant_api/rawclient.py b/homeassistant_api/rawclient.py index 4e1c4af3..1c0e7161 100644 --- a/homeassistant_api/rawclient.py +++ b/homeassistant_api/rawclient.py @@ -83,6 +83,7 @@ def request( path, method="GET", headers: Dict[str, str] = None, + decode_bytes: bool = False, **kwargs, ) -> Any: """Base method for making requests to the api""" @@ -101,12 +102,12 @@ def request( raise RequestTimeoutError( f'Home Assistant did not respond in time (timeout: {kwargs.get("timeout", 300)} sec)' ) from err - return self.response_logic(resp) + return self.response_logic(response=resp, decode_bytes=decode_bytes) @classmethod - def response_logic(cls, response: ResponseType) -> Any: + def response_logic(cls, response: ResponseType, decode_bytes: bool = False) -> Any: """Processes responses from the API and formats them""" - return Processing(response=response).process() + return Processing(response=response, decode_bytes=decode_bytes).process() # API information methods def get_error_log(self) -> str: From b9c094ba62565f3a23bd71d205a5094673da7850 Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 30 Dec 2022 17:33:00 +0100 Subject: [PATCH 2/6] Fix default values --- homeassistant_api/processing.py | 2 +- homeassistant_api/rawclient.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant_api/processing.py b/homeassistant_api/processing.py index b9b5fd1d..31903181 100644 --- a/homeassistant_api/processing.py +++ b/homeassistant_api/processing.py @@ -37,7 +37,7 @@ class Processing: _response: AllResponseType _processors: ClassVar[Dict[str, Tuple[ProcessorType, ...]]] = {} - def __init__(self, response: AllResponseType, decode_bytes: bool = False) -> None: + def __init__(self, response: AllResponseType, decode_bytes: bool = True) -> None: self._response = response self._decode_bytes = decode_bytes diff --git a/homeassistant_api/rawclient.py b/homeassistant_api/rawclient.py index 1c0e7161..adaa7610 100644 --- a/homeassistant_api/rawclient.py +++ b/homeassistant_api/rawclient.py @@ -83,7 +83,7 @@ def request( path, method="GET", headers: Dict[str, str] = None, - decode_bytes: bool = False, + decode_bytes: bool = True, **kwargs, ) -> Any: """Base method for making requests to the api""" @@ -105,7 +105,7 @@ def request( return self.response_logic(response=resp, decode_bytes=decode_bytes) @classmethod - def response_logic(cls, response: ResponseType, decode_bytes: bool = False) -> Any: + def response_logic(cls, response: ResponseType, decode_bytes: bool = True) -> Any: """Processes responses from the API and formats them""" return Processing(response=response, decode_bytes=decode_bytes).process() From 7955b3cfb4a6983edc7e394806cb871a11de6b21 Mon Sep 17 00:00:00 2001 From: Keith Date: Sat, 31 Dec 2022 17:56:54 +0100 Subject: [PATCH 3/6] Fix double-decode --- homeassistant_api/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant_api/processing.py b/homeassistant_api/processing.py index 31903181..78f15a18 100644 --- a/homeassistant_api/processing.py +++ b/homeassistant_api/processing.py @@ -76,7 +76,7 @@ def process(self) -> Any: if async_ := isinstance(self._response, (ClientResponse, AsyncCachedResponse)): status_code = self._response.status _buffer = self._response.content._buffer - content = b"" if not _buffer else _buffer[0].decode() + content = b"" if not _buffer else _buffer[0] if self._decode_bytes: content = content.decode() elif isinstance(self._response, (Response, CachedResponse)): From cb144cedae70d18c24ccab9941cc8ae7ac4a5590 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 3 Jan 2023 14:18:19 +0100 Subject: [PATCH 4/6] Fix typechecks, after figuring out that they can be run locally using `poetry run mypy homeassistant_api --show-error-codes` --- homeassistant_api/errors.py | 6 ++++-- homeassistant_api/processing.py | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant_api/errors.py b/homeassistant_api/errors.py index 8fd59880..2a9e33dd 100644 --- a/homeassistant_api/errors.py +++ b/homeassistant_api/errors.py @@ -1,5 +1,7 @@ """Module for custom error classes""" +from typing import Union + class HomeassistantAPIError(BaseException): """Base class for custom errors""" @@ -40,10 +42,10 @@ class ParameterMissingError(HomeassistantAPIError): class InternalServerError(HomeassistantAPIError): """Error raised when Home Assistant says that it got itself in trouble.""" - def __init__(self, status_code: int, content: str) -> None: + def __init__(self, status_code: int, content: Union[str, bytes]) -> None: super().__init__( f"Home Assistant returned a response with an error status code {status_code!r}.\n" - f"{content}\n" + f"{content!r}\n" "If this happened, " "please report it at https://github.com/GrandMoff100/HomeAssistantAPI/issues " "with the request status code and the request content. Thanks!" diff --git a/homeassistant_api/processing.py b/homeassistant_api/processing.py index 78f15a18..ef327d67 100644 --- a/homeassistant_api/processing.py +++ b/homeassistant_api/processing.py @@ -73,17 +73,17 @@ def process_content(self, *, async_: bool = False) -> Any: def process(self) -> Any: """Validates the http status code before starting to process the repsonse content""" + content: Union[str, bytes] if async_ := isinstance(self._response, (ClientResponse, AsyncCachedResponse)): status_code = self._response.status _buffer = self._response.content._buffer content = b"" if not _buffer else _buffer[0] - if self._decode_bytes: - content = content.decode() elif isinstance(self._response, (Response, CachedResponse)): status_code = self._response.status_code content = self._response.content - if self._decode_bytes: - content = content.decode() + if self._decode_bytes and isinstance(content, bytes): + content = content.decode() + if status_code in (200, 201): return self.process_content(async_=async_) if status_code == 400: @@ -96,7 +96,7 @@ def process(self) -> Any: method = ( self._response.request.method if isinstance(self._response, (Response, CachedResponse)) - else self._response.method + else self._response.method # type: ignore ) raise MethodNotAllowedError(cast(str, method)) if status_code >= 500: From 3c9e93a054ce9609ede376d95282bbda48755da5 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 3 Jan 2023 14:27:25 +0100 Subject: [PATCH 5/6] Fix unrelated type error, rather than ignoring. For some reason, mypy didn't like the typechecking in the ternary operator, but is fine with the statement. --- homeassistant_api/processing.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/homeassistant_api/processing.py b/homeassistant_api/processing.py index ef327d67..2f8fedd3 100644 --- a/homeassistant_api/processing.py +++ b/homeassistant_api/processing.py @@ -93,11 +93,10 @@ def process(self) -> Any: if status_code == 404: raise EndpointNotFoundError(str(self._response.url)) if status_code == 405: - method = ( - self._response.request.method - if isinstance(self._response, (Response, CachedResponse)) - else self._response.method # type: ignore - ) + if isinstance(self._response, (Response, AsyncCachedResponse)): + method = self._response.request.method + else: + method = self._response.method raise MethodNotAllowedError(cast(str, method)) if status_code >= 500: raise InternalServerError(status_code, content) From d61b20a5e8a1c288a8562dd6a01583081f41b396 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 3 Jan 2023 19:52:07 +0100 Subject: [PATCH 6/6] Fix typo introduced in 3c9e93a --- homeassistant_api/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant_api/processing.py b/homeassistant_api/processing.py index 2f8fedd3..7a4439fe 100644 --- a/homeassistant_api/processing.py +++ b/homeassistant_api/processing.py @@ -93,7 +93,7 @@ def process(self) -> Any: if status_code == 404: raise EndpointNotFoundError(str(self._response.url)) if status_code == 405: - if isinstance(self._response, (Response, AsyncCachedResponse)): + if isinstance(self._response, (Response, CachedResponse)): method = self._response.request.method else: method = self._response.method