From 3a24bd1baef50390b2692fbf7a093e85493a0266 Mon Sep 17 00:00:00 2001 From: David Bonnes Date: Sat, 2 Nov 2024 16:43:35 +0000 Subject: [PATCH] mypy fixes --- src/evohomeasync2/__init__.py | 57 +++++++++++++++++++------------- src/evohomeasync2/client.py | 4 +-- src/evohomeasync2/location.py | 4 +-- tests/tests_rf/conftest.py | 4 +-- tests/tests_rf/helpers.py | 9 ++--- tests/tests_rf/test_token_mgr.py | 12 +++---- tests/tests_rf/test_v2_apis.py | 20 +++++------ tests/tests_rf/test_v2_task.py | 4 +-- tests/tests_rf/test_v2_urls.py | 28 ++++++++-------- 9 files changed, 77 insertions(+), 65 deletions(-) diff --git a/src/evohomeasync2/__init__.py b/src/evohomeasync2/__init__.py index 7295b4be..ac306142 100644 --- a/src/evohomeasync2/__init__.py +++ b/src/evohomeasync2/__init__.py @@ -47,7 +47,7 @@ class TokenManager(AbstractTokenManager): # used only by EvohomeClientOld - """A wrapper to expose the new EvohomeClient without a TokenManager.""" + """A TokenManager wrapper to help expose the new EvohomeClient.""" def __init__( self, @@ -76,8 +76,8 @@ async def save_access_token(self) -> None: class EvohomeClientNew: # requires a Token Manager """Provide a client to access the Honeywell TCC API.""" - _installation_config: _EvoListT | None = None # all locations - _user_information: _EvoDictT | None = None + _install_config: _EvoListT | None = None # all locations + _user_info: _EvoDictT | None = None def __init__( self, @@ -119,25 +119,31 @@ async def update( """ if reset_config: - self._user_information = None - self._installation_config = None + self._user_info = None + self._install_config = None - if not self._user_information: + if self._user_info is None: url = "userAccount" - self._user_information = await self.auth.get(url, schema=SCH_USER_ACCOUNT) + self._user_info = await self.auth.get(url, schema=SCH_USER_ACCOUNT) # type: ignore[assignment] - if not self._installation_config: - url = ( - f"location/installationInfo?userId={self._user_information[SZ_USER_ID]}" - ) + assert self._user_info is not None # mypy hint + + if self._install_config is None: + url = f"location/installationInfo?userId={self._user_info[SZ_USER_ID]}" url += "&includeTemperatureControlSystems=True" - self._installation_config = await self.auth.get(url, schema=SCH_FULL_CONFIG) + self._install_config = await self.auth.get(url, schema=SCH_FULL_CONFIG) # type: ignore[assignment] + + self._locations = None + self._location_by_id = None + + assert self._install_config is not None # mypy hint + if self._locations is None: self._locations = [] self._location_by_id = {} - for loc_config in self._installation_config: + for loc_config in self._install_config: loc = Location(self, loc_config) self._locations.append(loc) self._location_by_id[loc.id] = loc @@ -148,42 +154,47 @@ async def update( "limits by individually updating only the necessary locations." ) + assert self._locations is not None # mypy hint + if not dont_update_status: for loc in self._locations: await loc.update() - return self._installation_config + return self._install_config @property def user_information(self) -> _EvoDictT: """Return the information of the user account.""" - if not self._user_information: + if not self._user_info: raise exc.NoSystemConfigError( f"{self}: The account information is not (yet) available" ) - return convert_keys_to_snake_case(self._user_information) + return convert_keys_to_snake_case(self._user_info) @property def installation_config(self) -> _EvoListT: """Return the installation info (config) of all the user's locations.""" - if not self._installation_config: + if not self._install_config: raise exc.NoSystemConfigError( f"{self}: The installation information is not (yet) available" ) - return convert_keys_to_snake_case(self._installation_config) + return convert_keys_to_snake_case(self._install_config) @property def locations(self) -> list[Location]: """Return the list of locations.""" - if self._installation_config is None: + if self._install_config is None: raise exc.NoSystemConfigError( f"{self}: The installation information is not (yet) available" ) + + assert self._locations # mypy hint + return self._locations # Most users only have exactly one TCS, thus these convenience methods... @@ -295,22 +306,22 @@ def __init__( super().__init__(self._token_manager, debug=debug) @property - def access_token(self) -> str: # type: ignore[override] + def access_token(self) -> str: """Return the access_token attr.""" return self._token_manager.access_token @property - def access_token_expires(self) -> dt: # type: ignore[override] + def access_token_expires(self) -> dt: """Return the access_token_expires attr.""" return self._token_manager.access_token_expires @property - def refresh_token(self) -> str: # type: ignore[override] + def refresh_token(self) -> str: """Return the refresh_token attr.""" return self._token_manager.refresh_token @property - def username(self) -> str: # type: ignore[override] + def username(self) -> str: """Return the username attr.""" return self._token_manager.username diff --git a/src/evohomeasync2/client.py b/src/evohomeasync2/client.py index 5fd62d15..5c670405 100644 --- a/src/evohomeasync2/client.py +++ b/src/evohomeasync2/client.py @@ -211,10 +211,10 @@ async def cleanup( if cache_tokens: # restore cached tokens, if any await token_manager.load_access_token() - evo = EvohomeClientNew(websession, token_manager, debug=bool(debug)) + evo = EvohomeClientNew(token_manager, debug=bool(debug)) try: - await evo.login() + await evo.update() except exc.AuthenticationFailedError: await websession.close() raise diff --git a/src/evohomeasync2/location.py b/src/evohomeasync2/location.py index e9a39a32..01171c81 100644 --- a/src/evohomeasync2/location.py +++ b/src/evohomeasync2/location.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: import voluptuous as vol - from . import EvohomeClient + from . import EvohomeClientNew from .schema import _EvoDictT @@ -35,7 +35,7 @@ class Location(EntityBase): STATUS_SCHEMA: Final[vol.Schema] = SCH_LOCN_STATUS TYPE: Final = EntityType.LOC # type: ignore[misc] - def __init__(self, client: EvohomeClient, config: _EvoDictT) -> None: + def __init__(self, client: EvohomeClientNew, config: _EvoDictT) -> None: super().__init__( config[SZ_LOCATION_INFO][SZ_LOCATION_ID], client.auth, diff --git a/tests/tests_rf/conftest.py b/tests/tests_rf/conftest.py index 81a014a5..2fda6eb3 100644 --- a/tests/tests_rf/conftest.py +++ b/tests/tests_rf/conftest.py @@ -38,7 +38,7 @@ def patches_for_tests(monkeypatch: pytest.MonkeyPatch) -> None: if _DBG_USE_REAL_AIOHTTP: import aiohttp else: - from .faked_server import aiohttp + from .faked_server import aiohttp # type: ignore[no-redef] monkeypatch.setattr("evohomeasync.auth.aiohttp", aiohttp) monkeypatch.setattr("evohomeasync2.auth.aiohttp", aiohttp) @@ -52,7 +52,7 @@ async def client_session() -> AsyncGenerator[aiohttp.ClientSession, None]: if _DBG_USE_REAL_AIOHTTP: import aiohttp else: - from .faked_server import aiohttp + from .faked_server import aiohttp # type: ignore[no-redef] if _DBG_USE_REAL_AIOHTTP: client_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30)) diff --git a/tests/tests_rf/helpers.py b/tests/tests_rf/helpers.py index 2888a9f8..9a24cd3c 100644 --- a/tests/tests_rf/helpers.py +++ b/tests/tests_rf/helpers.py @@ -197,12 +197,13 @@ async def wait_for_comm_task_v2(evo: evo2.EvohomeClientNew, task_id: str) -> boo response = await should_work(evo, HTTPMethod.GET, url) assert isinstance(response, dict | list), response - if response["state"] == "Succeeded": # type: ignore[call-overload] + task = response[0] if isinstance(response, list) else response + + if task["state"] == "Succeeded": return True - if response["state"] in ("Created", "Running"): # type: ignore[call-overload] + if task["state"] in ("Created", "Running"): await asyncio.sleep(0.3) continue - else: - raise RuntimeError(f"Unexpected state: {response['state']}") + raise RuntimeError(f"Unexpected state: {task['state']}") diff --git a/tests/tests_rf/test_token_mgr.py b/tests/tests_rf/test_token_mgr.py index acd61048..acfc197e 100644 --- a/tests/tests_rf/test_token_mgr.py +++ b/tests/tests_rf/test_token_mgr.py @@ -157,20 +157,20 @@ async def _test_evo_update_00( evo = evohome_v2 - assert evo._user_information is None - assert evo._installation_config is None + assert evo._user_info is None + assert evo._install_config is None await evo.update(reset_config=False, dont_update_status=False) - assert evo._user_information - assert evo._installation_config + assert evo._user_info is not None + assert evo._install_config is not None # type: ignore[unreachable] assert evo.locations[0]._status == {} await evo.update(reset_config=True) - assert evo._user_information - assert evo._installation_config + assert evo._user_info is not None + assert evo._install_config is not None assert evo.locations[0]._status != {} diff --git a/tests/tests_rf/test_v2_apis.py b/tests/tests_rf/test_v2_apis.py index 68686702..08dd916b 100644 --- a/tests/tests_rf/test_v2_apis.py +++ b/tests/tests_rf/test_v2_apis.py @@ -25,14 +25,14 @@ ####################################################################################### -async def _test_basics_apis(evo: evo2.EvohomeClient) -> None: +async def _test_basics_apis(evo: evo2.EvohomeClientNew) -> None: """Test authentication, `user_account()` and `installation()`.""" # STEP 1: retrieve base data await evo.update(dont_update_status=False) - assert SCH_USER_ACCOUNT(evo._installation_config) - assert SCH_FULL_CONFIG(evo._user_information) + assert SCH_USER_ACCOUNT(evo._install_config) + assert SCH_FULL_CONFIG(evo._user_info) # STEP 4: Status, GET /location/{loc.id}/status for loc in evo.locations: @@ -42,7 +42,7 @@ async def _test_basics_apis(evo: evo2.EvohomeClient) -> None: pass -async def _test_sched__apis(evo: evo2.EvohomeClient) -> None: +async def _test_sched__apis(evo: evo2.EvohomeClientNew) -> None: """Test `get_schedule()` and `get_schedule()`.""" # STEP 1: retrieve base data @@ -70,7 +70,7 @@ async def _test_sched__apis(evo: evo2.EvohomeClient) -> None: assert False -async def _test_update_apis(evo: evo2.EvohomeClient) -> None: +async def _test_update_apis(evo: evo2.EvohomeClientNew) -> None: """Test `_update()` for DHW/zone.""" # STEP 1: retrieve config @@ -88,7 +88,7 @@ async def _test_update_apis(evo: evo2.EvohomeClient) -> None: pass -async def _test_system_apis(evo: evo2.EvohomeClient) -> None: +async def _test_system_apis(evo: evo2.EvohomeClientNew) -> None: """Test `set_mode()` for TCS.""" # STEP 1: retrieve base data @@ -114,22 +114,22 @@ async def _test_system_apis(evo: evo2.EvohomeClient) -> None: ####################################################################################### -async def test_basics(evohome_v2: evo2.EvohomeClient) -> None: +async def test_basics(evohome_v2: evo2.EvohomeClientNew) -> None: """Test authentication, `user_account()` and `installation()`.""" await _test_basics_apis(evohome_v2) -async def _test_sched_(evohome_v2: evo2.EvohomeClient) -> None: +async def _test_sched_(evohome_v2: evo2.EvohomeClientNew) -> None: """Test `get_schedule()` and `get_schedule()`.""" await _test_sched__apis(evohome_v2) -async def test_status(evohome_v2: evo2.EvohomeClient) -> None: +async def test_status(evohome_v2: evo2.EvohomeClientNew) -> None: """Test `_update()` for DHW/zone.""" await _test_update_apis(evohome_v2) -async def test_system(evohome_v2: evo2.EvohomeClient) -> None: +async def test_system(evohome_v2: evo2.EvohomeClientNew) -> None: """Test `set_mode()` for TCS""" try: diff --git a/tests/tests_rf/test_v2_task.py b/tests/tests_rf/test_v2_task.py index ceda9d99..080d6ce1 100644 --- a/tests/tests_rf/test_v2_task.py +++ b/tests/tests_rf/test_v2_task.py @@ -26,7 +26,7 @@ ####################################################################################### -async def _test_task_id(evo: evo2.EvohomeClient) -> None: +async def _test_task_id(evo: evo2.EvohomeClientNew) -> None: """Test the task_id returned when using the vendor's RESTful APIs. This test can be used to prove that JSON keys are can be camelCase or PascalCase. @@ -186,7 +186,7 @@ async def _test_task_id(evo: evo2.EvohomeClient) -> None: ####################################################################################### -async def test_task_id(evohome_v2: evo2.EvohomeClient) -> None: +async def test_task_id(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /location/{loc.id}/status""" if not _DBG_USE_REAL_AIOHTTP: diff --git a/tests/tests_rf/test_v2_urls.py b/tests/tests_rf/test_v2_urls.py index e5fc5770..86fc26ca 100644 --- a/tests/tests_rf/test_v2_urls.py +++ b/tests/tests_rf/test_v2_urls.py @@ -45,7 +45,7 @@ ####################################################################################### -async def _test_usr_account(evo: evo2.EvohomeClient) -> None: +async def _test_usr_account(evo: evo2.EvohomeClientNew) -> None: """Test /userAccount""" url = "userAccount" @@ -64,13 +64,13 @@ async def _test_usr_account(evo: evo2.EvohomeClient) -> None: ) -async def _test_all_config(evo: evo2.EvohomeClient) -> None: +async def _test_all_config(evo: evo2.EvohomeClientNew) -> None: """Test /location/installationInfo?userId={user_id}""" _ = await evo.update() # - url = f"location/installationInfo?userId={evo._user_information[SZ_USER_ID]}" + url = f"location/installationInfo?userId={evo._user_info[SZ_USER_ID]}" await should_work(evo, HTTPMethod.GET, url) url += "&includeTemperatureControlSystems=True" @@ -92,7 +92,7 @@ async def _test_all_config(evo: evo2.EvohomeClient) -> None: _ = await should_fail(evo, HTTPMethod.GET, url, status=HTTPStatus.NOT_FOUND) -async def _test_loc_status(evo: evo2.EvohomeClient) -> None: +async def _test_loc_status(evo: evo2.EvohomeClientNew) -> None: """Test /location/{loc.id}/status""" _ = await evo.update(dont_update_status=True) @@ -134,7 +134,7 @@ async def _test_loc_status(evo: evo2.EvohomeClient) -> None: ) -async def _test_tcs_mode(evo: evo2.EvohomeClient) -> None: +async def _test_tcs_mode(evo: evo2.EvohomeClientNew) -> None: """Test /temperatureControlSystem/{tcs.id}/mode""" _ = await evo.update(dont_update_status=True) @@ -196,7 +196,7 @@ async def _test_tcs_mode(evo: evo2.EvohomeClient) -> None: pass -async def _test_zone_mode(evo: evo2.EvohomeClient) -> None: +async def _test_zone_mode(evo: evo2.EvohomeClientNew) -> None: """Test /temperatureZone/{zone.id}/heatSetpoint""" _ = await evo.update() @@ -213,7 +213,7 @@ async def _test_zone_mode(evo: evo2.EvohomeClient) -> None: url = f"{zone.TYPE}/{zone.id}/heatSetpoint" - heat_setpoint = { + heat_setpoint: dict[str, float | str | None] = { SZ_SETPOINT_MODE: ZoneMode.PERMANENT_OVERRIDE, SZ_HEAT_SETPOINT_VALUE: zone.temperature, # SZ_TIME_UNTIL: None, @@ -251,7 +251,7 @@ async def _test_zone_mode(evo: evo2.EvohomeClient) -> None: # TODO: Test sending bad schedule # TODO: Try with/without convert_to_put_schedule() -async def _test_schedule(evo: evo2.EvohomeClient) -> None: +async def _test_schedule(evo: evo2.EvohomeClientNew) -> None: """Test /{x.TYPE}/{x.id}/schedule (of a zone)""" _ = await evo.update() @@ -302,7 +302,7 @@ async def _test_schedule(evo: evo2.EvohomeClient) -> None: ####################################################################################### -async def test_usr_account(evohome_v2: evo2.EvohomeClient) -> None: +async def test_usr_account(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /userAccount""" try: @@ -314,19 +314,19 @@ async def test_usr_account(evohome_v2: evo2.EvohomeClient) -> None: pytest.skip("Unable to authenticate") -async def test_all_config(evohome_v2: evo2.EvohomeClient) -> None: +async def test_all_config(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /location/installationInfo""" await _test_all_config(evohome_v2) -async def test_loc_status(evohome_v2: evo2.EvohomeClient) -> None: +async def test_loc_status(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /location/{loc.id}/status""" await _test_loc_status(evohome_v2) -async def test_tcs_mode(evohome_v2: evo2.EvohomeClient) -> None: +async def test_tcs_mode(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /temperatureControlSystem/{tcs.id}/mode""" try: @@ -338,7 +338,7 @@ async def test_tcs_mode(evohome_v2: evo2.EvohomeClient) -> None: pytest.skip("Mocked server API not implemented") -async def test_zone_mode(evohome_v2: evo2.EvohomeClient) -> None: +async def test_zone_mode(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /temperatureZone/{zone.id}/heatSetpoint""" try: @@ -350,7 +350,7 @@ async def test_zone_mode(evohome_v2: evo2.EvohomeClient) -> None: pytest.skip("Mocked server API not implemented") -async def test_schedule(evohome_v2: evo2.EvohomeClient) -> None: +async def test_schedule(evohome_v2: evo2.EvohomeClientNew) -> None: """Test /{x.TYPE}/{x.id}/schedule""" await _test_schedule(evohome_v2)