Skip to content

Commit

Permalink
Exceptions shoudl have Error suffix
Browse files Browse the repository at this point in the history
  • Loading branch information
zxdavb committed Aug 31, 2024
1 parent 82243e4 commit df2a452
Show file tree
Hide file tree
Showing 17 changed files with 76 additions and 70 deletions.
8 changes: 4 additions & 4 deletions src/evohomeasync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

from .base import EvohomeClient # noqa: F401
from .exceptions import ( # noqa: F401
AuthenticationFailed,
AuthenticationFailedError,
EvohomeError,
InvalidSchema,
RateLimitExceeded,
RequestFailed,
InvalidSchemaError,
RateLimitExceededError,
RequestFailedError,
)

__version__ = "1.0.5"
10 changes: 6 additions & 4 deletions src/evohomeasync/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ async def get_temperatures(

# harden code against unexpected schema (JSON structure)
except (LookupError, TypeError, ValueError) as err:
raise exc.InvalidSchema(str(err)) from err
raise exc.InvalidSchemaError(str(err)) from err
return result # type: ignore[return-value]

async def get_system_modes(self) -> NoReturn:
Expand Down Expand Up @@ -374,12 +374,14 @@ async def _get_zone(self, id_or_name: _ZoneIdT | _ZoneNameT) -> _DeviceDictT:
device_dict = self.named_devices.get(id_or_name)

if device_dict is None:
raise exc.InvalidSchema(
raise exc.InvalidSchemaError(
f"No zone {id_or_name} in location {self.location_id}"
)

if (model := device_dict[SZ_THERMOSTAT_MODEL_TYPE]) != SZ_EMEA_ZONE:
raise exc.InvalidSchema(f"Zone {id_or_name} is not an EMEA_ZONE: {model}")
raise exc.InvalidSchemaError(
f"Zone {id_or_name} is not an EMEA_ZONE: {model}"
)

return device_dict

Expand Down Expand Up @@ -446,7 +448,7 @@ async def _get_dhw(self) -> _DeviceDictT:
ret: _DeviceDictT = device
return ret

raise exc.InvalidSchema(f"No DHW in location {self.location_id}")
raise exc.InvalidSchemaError(f"No DHW in location {self.location_id}")

async def _set_dhw(
self,
Expand Down
10 changes: 5 additions & 5 deletions src/evohomeasync/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,16 +196,16 @@ async def make_request(

except aiohttp.ClientResponseError as err:
if response.method == HTTPMethod.POST: # POST only used when authenticating
raise exc.AuthenticationFailed( # includes TOO_MANY_REQUESTS
raise exc.AuthenticationFailedError( # includes TOO_MANY_REQUESTS
str(err), status=err.status
) from err
if response.status == HTTPStatus.TOO_MANY_REQUESTS:
raise exc.RateLimitExceeded(str(err), status=err.status) from err
raise exc.RequestFailed(str(err), status=err.status) from err
raise exc.RateLimitExceededError(str(err), status=err.status) from err
raise exc.RequestFailedError(str(err), status=err.status) from err

except aiohttp.ClientError as err: # using response causes UnboundLocalError
if method == HTTPMethod.POST: # POST only used when authenticating
raise exc.AuthenticationFailed(str(err)) from err
raise exc.RequestFailed(str(err)) from err
raise exc.AuthenticationFailedError(str(err)) from err
raise exc.RequestFailedError(str(err)) from err

return response
8 changes: 4 additions & 4 deletions src/evohomeasync/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ class DeprecationError(EvohomeBaseError):
"""The method or property has changed, or is otherwise deprecated."""


class InvalidSchema(EvohomeError):
class InvalidSchemaError(EvohomeError):
"""The config/status JSON is invalid (e.g. missing an entity Id)."""


class RequestFailed(EvohomeError):
class RequestFailedError(EvohomeError):
"""The API request failed for some reason (no/invalid/unexpected response).
Could be caused by any aiohttp.ClientError, for example: ConnectionError. If the
Expand All @@ -36,11 +36,11 @@ def __init__(self, message: str, status: int | None = None) -> None:
self.status = status # iff cause was aiohttp.ClientResponseError


class RateLimitExceeded(RequestFailed):
class RateLimitExceededError(RequestFailedError):
"""API request failed because the vendor's API rate limit was exceeded."""


class AuthenticationFailed(RequestFailed):
class AuthenticationFailedError(RequestFailedError):
"""Unable to authenticate (unable to obtain an access token).
The cause could be any FailedRequest, including RateLimitExceeded.
Expand Down
12 changes: 6 additions & 6 deletions src/evohomeasync2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
from .broker import Broker # noqa: F401
from .controlsystem import ControlSystem # noqa: F401
from .exceptions import ( # noqa: F401
AuthenticationFailed,
AuthenticationFailedError,
DeprecationError,
EvohomeError,
InvalidParameter,
InvalidSchedule,
InvalidSchema,
InvalidParameterError,
InvalidScheduleError,
InvalidSchemaError,
NoSingleTcsError,
RateLimitExceeded,
RequestFailed,
RateLimitExceededError,
RequestFailedError,
)
from .gateway import Gateway # noqa: F401
from .hotwater import HotWater # noqa: F401
Expand Down
2 changes: 1 addition & 1 deletion src/evohomeasync2/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ async def login(self) -> None:
try: # the cached access_token may be valid, but is not authorized
await self.user_account()

except exc.AuthenticationFailed as err:
except exc.AuthenticationFailedError as err:
if err.status != HTTPStatus.UNAUTHORIZED: # or not self.access_token:
raise

Expand Down
18 changes: 9 additions & 9 deletions src/evohomeasync2/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async def _update_access_token(self) -> None:
CREDS_REFRESH_TOKEN | {SZ_REFRESH_TOKEN: self.refresh_token}
)

except exc.AuthenticationFailed as err:
except exc.AuthenticationFailedError as err:
if err.status != HTTPStatus.BAD_REQUEST: # e.g. invalid tokens
raise

Expand Down Expand Up @@ -203,7 +203,7 @@ async def _obtain_access_token(self, credentials: dict[str, str]) -> None:
self._token_data_from_api(token_data)

except (KeyError, TypeError) as err:
raise exc.AuthenticationFailed(
raise exc.AuthenticationFailedError(
f"Invalid response from server: {err}"
) from err

Expand All @@ -222,17 +222,17 @@ async def _post_access_token_request(
# <title>Authorize error <h1>Authorization failed
# <p>The authorization server have encoutered an error while processing...
content = await response.text()
raise exc.AuthenticationFailed(
raise exc.AuthenticationFailedError(
f"Server response is not JSON: {HTTPMethod.POST} {AUTH_URL}: {content}"
) from err

except aiohttp.ClientResponseError as err:
if hint := _ERR_MSG_LOOKUP_AUTH.get(err.status):
raise exc.AuthenticationFailed(hint, status=err.status) from err
raise exc.AuthenticationFailed(str(err), status=err.status) from err
raise exc.AuthenticationFailedError(hint, status=err.status) from err
raise exc.AuthenticationFailedError(str(err), status=err.status) from err

except aiohttp.ClientError as err: # e.g. ClientConnectionError
raise exc.AuthenticationFailed(str(err)) from err
raise exc.AuthenticationFailedError(str(err)) from err

@abstractmethod
async def save_access_token(self) -> None: # HA: api
Expand Down Expand Up @@ -284,11 +284,11 @@ async def request(

except aiohttp.ClientResponseError as err:
if hint := _ERR_MSG_LOOKUP_BASE.get(err.status):
raise exc.RequestFailed(hint, status=err.status) from err
raise exc.RequestFailed(str(err), status=err.status) from err
raise exc.RequestFailedError(hint, status=err.status) from err
raise exc.RequestFailedError(str(err), status=err.status) from err

except aiohttp.ClientError as err: # e.g. ClientConnectionError
raise exc.RequestFailed(str(err)) from err
raise exc.RequestFailedError(str(err)) from err

async def _headers(self) -> dict[str, str]:
"""Ensure the Authorization Header has a valid Access Token."""
Expand Down
2 changes: 1 addition & 1 deletion src/evohomeasync2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ async def cleanup(

try:
await evo.login()
except exc.AuthenticationFailed:
except exc.AuthenticationFailedError:
await websession.close()
raise

Expand Down
6 changes: 3 additions & 3 deletions src/evohomeasync2/controlsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def __init__(self, gateway: Gateway, config: _EvoDictT, /) -> None:
for zon_config in config[SZ_ZONES]:
try:
zone = Zone(self, zon_config)
except exc.InvalidSchema as err:
except exc.InvalidSchemaError as err:
self._logger.warning(
f"{self}: zone_id='{zon_config[SZ_ZONE_ID]}' ignored: {err}"
)
Expand Down Expand Up @@ -227,7 +227,7 @@ async def set_mode(self, mode: SystemMode, /, *, until: dt | None = None) -> Non
request: _EvoDictT

if mode not in [m[SZ_SYSTEM_MODE] for m in self.allowedSystemModes]:
raise exc.InvalidParameter(f"{self}: Unsupported/unknown mode: {mode}")
raise exc.InvalidParameterError(f"{self}: Unsupported/unknown mode: {mode}")

if until is None:
request = {
Expand Down Expand Up @@ -321,7 +321,7 @@ async def get_schedules(self) -> _ScheduleT:
async def get_schedule(child: HotWater | Zone) -> _ScheduleT:
try:
return await child.get_schedule()
except exc.InvalidSchedule:
except exc.InvalidScheduleError:
self._logger.warning(
f"Ignoring schedule of {child._id} ({child.name}): missing/invalid"
)
Expand Down
12 changes: 6 additions & 6 deletions src/evohomeasync2/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ class DeprecationError(EvohomeBaseError):
"""The method or property has changed, or is otherwise deprecated."""


class InvalidSchema(EvohomeError):
class InvalidSchemaError(EvohomeError):
"""The config/status JSON is invalid (e.g. missing an entity Id)."""


class InvalidParameter(EvohomeError):
class InvalidParameterError(EvohomeError):
"""The supplied parameter(s) is/are invalid (e.g. unknown/unsupported mode)."""


class InvalidSchedule(InvalidSchema):
class InvalidScheduleError(InvalidSchemaError):
"""The schedule has an invalid schema."""


class NoSingleTcsError(EvohomeError):
"""There is not exactly one TCS in the user's installation."""


class RequestFailed(EvohomeError):
class RequestFailedError(EvohomeError):
"""The API request failed for some reason (no/invalid/unexpected response).
Could be caused by any aiohttp.ClientError, for example: ConnectionError. If the
Expand All @@ -48,11 +48,11 @@ def __init__(self, message: str, status: None | int = None) -> None:
self.status = status # iff cause was aiohttp.ClientResponseError


class RateLimitExceeded(RequestFailed):
class RateLimitExceededError(RequestFailedError):
"""API request failed because the vendor's API rate limit was exceeded."""


class AuthenticationFailed(RequestFailed):
class AuthenticationFailedError(RequestFailedError):
"""Unable to authenticate (unable to obtain an access token).
The cause could be any FailedRequest, including RateLimitExceeded.
Expand Down
6 changes: 3 additions & 3 deletions src/evohomeasync2/schema/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,12 @@ def convert_to_put_schedule(schedule: _EvoDictT) -> _EvoDictT:
"""

if not schedule:
raise exc.InvalidSchedule(f"Null schedule: {schedule}")
raise exc.InvalidScheduleError(f"Null schedule: {schedule}")

try:
SCH_GET_SCHEDULE(schedule)
except vol.Invalid as err:
raise exc.InvalidSchedule(f"Invalid schedule: {err}") from err
raise exc.InvalidScheduleError(f"Invalid schedule: {err}") from err

put_schedule: dict[str, _EvoListT] = {}
put_schedule[pascal_case(SZ_DAILY_SCHEDULES)] = []
Expand Down Expand Up @@ -186,7 +186,7 @@ def convert_to_get_schedule(schedule: _EvoDictT) -> _EvoDictT:
try:
SCH_PUT_SCHEDULE(schedule)
except vol.Invalid as err:
raise exc.InvalidSchedule(f"Invalid schedule: {err}") from err
raise exc.InvalidScheduleError(f"Invalid schedule: {err}") from err

get_schedule: dict[str, _EvoListT] = {}
get_schedule[SZ_DAILY_SCHEDULES] = []
Expand Down
22 changes: 13 additions & 9 deletions src/evohomeasync2/zone.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,15 @@ async def get_schedule(self) -> _EvoDictT:
f"{self.TYPE}/{self._id}/schedule", schema=self.SCH_SCHEDULE_GET
) # type: ignore[assignment]

except exc.RequestFailed as err:
except exc.RequestFailedError as err:
if err.status == HTTPStatus.BAD_REQUEST:
raise exc.InvalidSchedule(
raise exc.InvalidScheduleError(
f"{self}: No Schedule / Schedule is invalid"
) from err
raise exc.RequestFailed(f"{self}: Unexpected error") from err
raise exc.RequestFailedError(f"{self}: Unexpected error") from err

except vol.Invalid as err:
raise exc.InvalidSchedule(
raise exc.InvalidScheduleError(
f"{self}: No Schedule / Schedule is invalid"
) from err

Expand All @@ -217,16 +217,20 @@ async def set_schedule(self, schedule: _EvoDictT | str) -> None:
try:
json.dumps(schedule)
except (OverflowError, TypeError, ValueError) as err:
raise exc.InvalidSchedule(f"{self}: Invalid schedule: {err}") from err
raise exc.InvalidScheduleError(
f"{self}: Invalid schedule: {err}"
) from err

elif isinstance(schedule, str):
try:
schedule = json.loads(schedule)
except json.JSONDecodeError as err:
raise exc.InvalidSchedule(f"{self}: Invalid schedule: {err}") from err
raise exc.InvalidScheduleError(
f"{self}: Invalid schedule: {err}"
) from err

else:
raise exc.InvalidSchedule(
raise exc.InvalidScheduleError(
f"{self}: Invalid schedule type: {type(schedule)}"
)

Expand Down Expand Up @@ -263,11 +267,11 @@ def __init__(self, tcs: ControlSystem, config: _EvoDictT, /) -> None:
super().__init__(config[SZ_ZONE_ID], tcs, config)

if not self.modelType or self.modelType == ZoneModelType.UNKNOWN:
raise exc.InvalidSchema(
raise exc.InvalidSchemaError(
f"{self}: Invalid model type '{self.modelType}' (is it a ghost zone?)"
)
if not self.zoneType or self.zoneType == ZoneType.UNKNOWN:
raise exc.InvalidSchema(
raise exc.InvalidSchemaError(
f"{self}: Invalid zone type '{self.zoneType}' (is it a ghost zone?)"
)

Expand Down
2 changes: 1 addition & 1 deletion tests/tests_rf/test_v1_apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ async def test_client_apis(
await _test_client_apis(
await instantiate_client_v1(*user_credentials, session=session)
)
except evohome.AuthenticationFailed:
except evohome.AuthenticationFailedError:
pytest.skip("Unable to authenticate")
4 changes: 2 additions & 2 deletions tests/tests_rf/test_v1_xxxx.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async def test_locations(
await _test_url_locations(
await instantiate_client_v1(*user_credentials, session=session)
)
except evohome.AuthenticationFailed as err:
except evohome.AuthenticationFailedError as err:
pytest.skip(f"Unable to authenticate: {err}")


Expand All @@ -86,7 +86,7 @@ async def test_client_apis(
await _test_client_apis(
await instantiate_client_v1(*user_credentials, session=session)
)
except evohome.AuthenticationFailed:
except evohome.AuthenticationFailedError:
pytest.skip("Unable to authenticate")


Expand Down
Loading

0 comments on commit df2a452

Please sign in to comment.