From dec54488e8a0c7eca1cb228668c0740fd7f90822 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 17 Nov 2021 09:07:01 +0100 Subject: [PATCH] Enable basic type checking for cloud (#55337) * Enable basic type checking for cloud * Update mypy settings * Address review comment * Fix rebase mistakes * Correct decorator order --- homeassistant/components/cloud/client.py | 9 +++--- homeassistant/components/cloud/http_api.py | 34 +++++++++++----------- homeassistant/components/cloud/prefs.py | 1 + homeassistant/components/cloud/utils.py | 12 +++++--- mypy.ini | 3 -- script/hassfest/mypy_config.py | 1 - 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index 5a10e1d1e5c..5f3abe521af 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -42,12 +42,13 @@ class CloudClient(Interface): self._websession = websession self.google_user_config = google_user_config self.alexa_user_config = alexa_user_config - self._alexa_config = None - self._google_config = None + self._alexa_config: alexa_config.AlexaConfig | None = None + self._google_config: google_config.CloudGoogleConfig | None = None @property def base_path(self) -> Path: """Return path to base dir.""" + assert self._hass.config.config_dir is not None return Path(self._hass.config.config_dir) @property @@ -56,7 +57,7 @@ class CloudClient(Interface): return self._prefs @property - def loop(self) -> asyncio.BaseEventLoop: + def loop(self) -> asyncio.AbstractEventLoop: """Return client loop.""" return self._hass.loop @@ -66,7 +67,7 @@ class CloudClient(Interface): return self._websession @property - def aiohttp_runner(self) -> aiohttp.web.AppRunner: + def aiohttp_runner(self) -> aiohttp.web.AppRunner | None: """Return client webinterface aiohttp application.""" return self._hass.http.runner diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index c18db2ac683..cd682057266 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -264,8 +264,8 @@ class CloudForgotPasswordView(HomeAssistantView): return self.json_message("ok") -@websocket_api.async_response @websocket_api.websocket_command({vol.Required("type"): "cloud/status"}) +@websocket_api.async_response async def websocket_cloud_status(hass, connection, msg): """Handle request for account info. @@ -298,8 +298,8 @@ def _require_cloud_login(handler): @_require_cloud_login -@websocket_api.async_response @websocket_api.websocket_command({vol.Required("type"): "cloud/subscription"}) +@websocket_api.async_response async def websocket_subscription(hass, connection, msg): """Handle request for account info.""" cloud = hass.data[DOMAIN] @@ -315,7 +315,6 @@ async def websocket_subscription(hass, connection, msg): @_require_cloud_login -@websocket_api.async_response @websocket_api.websocket_command( { vol.Required("type"): "cloud/update_prefs", @@ -331,6 +330,7 @@ async def websocket_subscription(hass, connection, msg): ), } ) +@websocket_api.async_response async def websocket_update_prefs(hass, connection, msg): """Handle request for account info.""" cloud = hass.data[DOMAIN] @@ -365,14 +365,14 @@ async def websocket_update_prefs(hass, connection, msg): @_require_cloud_login -@websocket_api.async_response -@_ws_handle_cloud_errors @websocket_api.websocket_command( { vol.Required("type"): "cloud/cloudhook/create", vol.Required("webhook_id"): str, } ) +@websocket_api.async_response +@_ws_handle_cloud_errors async def websocket_hook_create(hass, connection, msg): """Handle request for account info.""" cloud = hass.data[DOMAIN] @@ -381,14 +381,14 @@ async def websocket_hook_create(hass, connection, msg): @_require_cloud_login -@websocket_api.async_response -@_ws_handle_cloud_errors @websocket_api.websocket_command( { vol.Required("type"): "cloud/cloudhook/delete", vol.Required("webhook_id"): str, } ) +@websocket_api.async_response +@_ws_handle_cloud_errors async def websocket_hook_delete(hass, connection, msg): """Handle request for account info.""" cloud = hass.data[DOMAIN] @@ -430,9 +430,9 @@ async def _account_data(cloud): @websocket_api.require_admin @_require_cloud_login +@websocket_api.websocket_command({"type": "cloud/remote/connect"}) @websocket_api.async_response @_ws_handle_cloud_errors -@websocket_api.websocket_command({"type": "cloud/remote/connect"}) async def websocket_remote_connect(hass, connection, msg): """Handle request for connect remote.""" cloud = hass.data[DOMAIN] @@ -442,9 +442,9 @@ async def websocket_remote_connect(hass, connection, msg): @websocket_api.require_admin @_require_cloud_login +@websocket_api.websocket_command({"type": "cloud/remote/disconnect"}) @websocket_api.async_response @_ws_handle_cloud_errors -@websocket_api.websocket_command({"type": "cloud/remote/disconnect"}) async def websocket_remote_disconnect(hass, connection, msg): """Handle request for disconnect remote.""" cloud = hass.data[DOMAIN] @@ -454,9 +454,9 @@ async def websocket_remote_disconnect(hass, connection, msg): @websocket_api.require_admin @_require_cloud_login +@websocket_api.websocket_command({"type": "cloud/google_assistant/entities"}) @websocket_api.async_response @_ws_handle_cloud_errors -@websocket_api.websocket_command({"type": "cloud/google_assistant/entities"}) async def google_assistant_list(hass, connection, msg): """List all google assistant entities.""" cloud = hass.data[DOMAIN] @@ -479,8 +479,6 @@ async def google_assistant_list(hass, connection, msg): @websocket_api.require_admin @_require_cloud_login -@websocket_api.async_response -@_ws_handle_cloud_errors @websocket_api.websocket_command( { "type": "cloud/google_assistant/entities/update", @@ -491,6 +489,8 @@ async def google_assistant_list(hass, connection, msg): vol.Optional("disable_2fa"): bool, } ) +@websocket_api.async_response +@_ws_handle_cloud_errors async def google_assistant_update(hass, connection, msg): """Update google assistant config.""" cloud = hass.data[DOMAIN] @@ -507,9 +507,9 @@ async def google_assistant_update(hass, connection, msg): @websocket_api.require_admin @_require_cloud_login +@websocket_api.websocket_command({"type": "cloud/alexa/entities"}) @websocket_api.async_response @_ws_handle_cloud_errors -@websocket_api.websocket_command({"type": "cloud/alexa/entities"}) async def alexa_list(hass, connection, msg): """List all alexa entities.""" cloud = hass.data[DOMAIN] @@ -532,8 +532,6 @@ async def alexa_list(hass, connection, msg): @websocket_api.require_admin @_require_cloud_login -@websocket_api.async_response -@_ws_handle_cloud_errors @websocket_api.websocket_command( { "type": "cloud/alexa/entities/update", @@ -541,6 +539,8 @@ async def alexa_list(hass, connection, msg): vol.Optional("should_expose"): vol.Any(None, bool), } ) +@websocket_api.async_response +@_ws_handle_cloud_errors async def alexa_update(hass, connection, msg): """Update alexa entity config.""" cloud = hass.data[DOMAIN] @@ -557,8 +557,8 @@ async def alexa_update(hass, connection, msg): @websocket_api.require_admin @_require_cloud_login -@websocket_api.async_response @websocket_api.websocket_command({"type": "cloud/alexa/sync"}) +@websocket_api.async_response async def alexa_sync(hass, connection, msg): """Sync with Alexa.""" cloud = hass.data[DOMAIN] @@ -581,8 +581,8 @@ async def alexa_sync(hass, connection, msg): connection.send_error(msg["id"], ws_const.ERR_UNKNOWN_ERROR, "Unknown error") -@websocket_api.async_response @websocket_api.websocket_command({"type": "cloud/thingtalk/convert", "query": str}) +@websocket_api.async_response async def thingtalk_convert(hass, connection, msg): """Convert a query.""" cloud = hass.data[DOMAIN] diff --git a/homeassistant/components/cloud/prefs.py b/homeassistant/components/cloud/prefs.py index bb76ca3b669..edd0e5ddda4 100644 --- a/homeassistant/components/cloud/prefs.py +++ b/homeassistant/components/cloud/prefs.py @@ -283,6 +283,7 @@ class CloudPreferences: user = await self._hass.auth.async_create_system_user( "Home Assistant Cloud", [GROUP_ID_ADMIN] ) + assert user is not None await self.async_update(cloud_user=user.id) return user.id diff --git a/homeassistant/components/cloud/utils.py b/homeassistant/components/cloud/utils.py index 715a8119af9..5908a0ac816 100644 --- a/homeassistant/components/cloud/utils.py +++ b/homeassistant/components/cloud/utils.py @@ -9,13 +9,17 @@ from aiohttp import payload, web def aiohttp_serialize_response(response: web.Response) -> dict[str, Any]: """Serialize an aiohttp response to a dictionary.""" if (body := response.body) is None: - pass + body_decoded = None elif isinstance(body, payload.StringPayload): # pylint: disable=protected-access - body = body._value.decode(body.encoding) + body_decoded = body._value.decode(body.encoding) elif isinstance(body, bytes): - body = body.decode(response.charset or "utf-8") + body_decoded = body.decode(response.charset or "utf-8") else: raise ValueError("Unknown payload encoding") - return {"status": response.status, "body": body, "headers": dict(response.headers)} + return { + "status": response.status, + "body": body_decoded, + "headers": dict(response.headers), + } diff --git a/mypy.ini b/mypy.ini index 4e374c8b1ae..9c7c481e986 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1670,9 +1670,6 @@ ignore_errors = true [mypy-homeassistant.components.climacell.*] ignore_errors = true -[mypy-homeassistant.components.cloud.*] -ignore_errors = true - [mypy-homeassistant.components.config.*] ignore_errors = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 7d289984335..cf1faadc72e 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -17,7 +17,6 @@ IGNORED_MODULES: Final[list[str]] = [ "homeassistant.components.awair.*", "homeassistant.components.blueprint.*", "homeassistant.components.climacell.*", - "homeassistant.components.cloud.*", "homeassistant.components.config.*", "homeassistant.components.conversation.*", "homeassistant.components.deconz.*",