8000 Rename and deprecate exception classes (#739) · python-kasa/python-kasa@8c39e81 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8c39e81

Browse files
authored
Rename and deprecate exception classes (#739)
# Public # SmartDeviceException -> KasaException UnsupportedDeviceException(SmartDeviceException) -> UnsupportedDeviceError(KasaException) TimeoutException(SmartDeviceException, asyncio.TimeoutError) -> TimeoutError(KasaException, asyncio.TimeoutError) Add new exception for error codes -> DeviceError(KasaException) AuthenticationException(SmartDeviceException) -> AuthenticationError(DeviceError) # Internal # RetryableException(SmartDeviceException) -> _RetryableError(DeviceError) ConnectionException(SmartDeviceException) -> _ConnectionError(KasaException)
1 parent 4beff22 commit 8c39e81

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+390
-358
lines changed

devtools/dump_devinfo.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222

2323
from devtools.helpers.smartrequests import SmartRequest, get_component_requests
2424
from kasa import (
25-
AuthenticationException,
25+
AuthenticationError,
2626
Credentials,
2727
Device,
2828
Discover,
29-
SmartDeviceException,
30-
TimeoutException,
29+
KasaException,
30+
TimeoutError,
3131
)
3232
from kasa.discover import DiscoveryResult
3333
from kasa.exceptions import SmartErrorCode
@@ -303,19 +303,16 @@ async def _make_requests_or_exit(
303303
for method, result in responses.items():
304304
final[method] = result
305305
return final
306-
except AuthenticationException as ex:
306+
except AuthenticationError as ex:
307307
_echo_error(
308308
f"Unable to query the device due to an authentication error: {ex}",
309309
)
310310
exit(1)
311-
except SmartDeviceException as ex:
311+
except KasaException as ex:
312312
_echo_error(
313313
f"Unable to query {name} at once: {ex}",
314314
)
315-
if (
316-
isinstance(ex, TimeoutException)
317-
or ex.error_code == SmartErrorCode.SESSION_TIMEOUT_ERROR
318-
):
315+
if isinstance(ex, TimeoutError):
319316
_echo_error(
320317
"Timeout, try reducing the batch size via --batch-size option.",
321318
)
@@ -400,7 +397,7 @@ async def get_smart_fixture(device: SmartDevice, batch_size: int):
400397
response = await device.protocol.query(
401398
SmartRequest._create_request_dict(test_call.request)
402399
)
403-
except AuthenticationException as ex:
400+
except AuthenticationError as ex:
404401
_echo_error(
405402
f"Unable to query the device due to an authentication error: {ex}",
406403
)

docs/source/design.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ The classes providing this functionality are:
100100
- :class:`KlapTransport <kasa.klaptransport.KlapTransport>`
101101
- :class:`KlapTransportV2 <kasa.klaptransport.KlapTransportV2>`
102102

103+
Errors and Exceptions
104+
*********************
105+
106+
The base exception for all library errors is :class:`KasaException <kasa.exceptions.KasaException>`.
107+
108+
- If the device returns an error the library raises a :class:`DeviceError <kasa.exceptions.DeviceError>` which will usually contain an ``error_code`` with the detail.
109+
- If the device fails to authenticate the library raises an :class:`AuthenticationError <kasa.exceptions.AuthenticationError>` which is derived
110+
from :class:`DeviceError <kasa.exceptions.DeviceError>` and could contain an ``error_code`` depending on the type of failure.
111+
- If the library encounters and unsupported deviceit raises an :class:`UnsupportedDeviceError <kasa.exceptions.UnsupportedDeviceError>`.
112+
- If the device fails to respond within a timeout the library raises a :class:`TimeoutError <kasa.exceptions.TimeoutError>`.
113+
- All other failures will raise the base :class:`KasaException <kasa.exceptions.KasaException>` class.
103114

104115
API documentation for modules
105116
*****************************
@@ -154,3 +165,26 @@ API documentation for protocols and transports
154165
:members:
155166
:inherited-members:
156167
:undoc-members:
168+
169+
API documentation for errors and exceptions
170+
*******************************************
171+
172+
.. autoclass:: kasa.exceptions.KasaException
173+
:members:
174+
:undoc-members:
175+
176+
.. autoclass:: kasa.exceptions.DeviceError
177+
:members:
178+
:undoc-members:
179+
180+
.. autoclass:: kasa.exceptions.AuthenticationError
181+
:members:
182+
:undoc-members:
183+
184+
.. autoclass:: kasa.exceptions.UnsupportedDeviceError
185+
:members:
186+
:undoc-members:
187+
188+
.. autoclass:: kasa.exceptions.TimeoutError
189+
:members:
190+
:undoc-members:

docs/source/smartdevice.rst

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Methods changing the state of the device do not invalidate the cache (i.e., ther
2424
You can assume that the operation has succeeded if no exception is raised.
2525
These methods will return the device response, which can be useful for some use cases.
2626

27-
Errors are raised as :class:`SmartDeviceException` instances for the library user to handle.
27+
Errors are raised as :class:`KasaException` instances for the library user to handle.
2828

2929
Simple example script showing some functionality for legacy devices:
3030

@@ -154,15 +154,3 @@ API documentation
154154
.. autoclass:: Credentials
155155
:members:
156156
:undoc-members:
157-
158-
.. autoclass:: SmartDeviceException
159-
:members:
160-
:undoc-members:
161-
162-
.. autoclass:: AuthenticationException
163-
:members:
164-
:undoc-members:
165-
166-
.. autoclass:: UnsupportedDeviceException
167-
:members:
168-
:undoc-members:

kasa/__init__.py

Lines changed: 27 additions & 9 deletions
"Device",
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
For device type specific actions `SmartBulb`, `SmartPlug`, or `SmartStrip`
99
should be used instead.
1010
11-
Module-specific errors are raised as `SmartDeviceException` and are expected
11+
Module-specific errors are raised as `KasaException` and are expected
1212
to be handled by the user of the library.
1313
"""
1414
from importlib.metadata import version
@@ -28,10 +28,11 @@
2828
from kasa.discover import Discover
2929
from kasa.emeterstatus import EmeterStatus
3030
from kasa.exceptions import (
31-
AuthenticationException,
32-
SmartDeviceException,
33-
TimeoutException,
34-
UnsupportedDeviceException,
31+
AuthenticationError,
32+
DeviceError,
33+
KasaException,
34+
TimeoutError,
35+
UnsupportedDeviceError,
3536
)
3637
from kasa.feature import Feature, FeatureType
3738
from kasa.iot.iotbulb import BulbPreset, TurnOnBehavior, TurnOnBehaviors
@@ -61,10 +62,11 @@
6162
6263
"Bulb",
6364
"Plug",
64-
"SmartDeviceException",
65-
"AuthenticationException",
66-
"UnsupportedDeviceException",
67-
"TimeoutException",
65+
"KasaException",
66+
"AuthenticationError",
67+
"DeviceError",
68+
"UnsupportedDeviceError",
69+
"TimeoutError",
6870
"Credentials",
6971
"DeviceConfig",
7072
"ConnectionType",
@@ -84,6 +86,12 @@
8486
"SmartDimmer": iot.IotDimmer,
8587
"SmartBulbPreset": BulbPreset,
8688
}
89+
deprecated_exceptions = {
90+
"SmartDeviceException": KasaException,
91+
"UnsupportedDeviceException": UnsupportedDeviceError,
92+
"AuthenticationException": AuthenticationError,
93+
"TimeoutException": TimeoutError,
94+
}
8795

8896

8997
def __getattr__(name):
@@ -101,6 +109,11 @@ def __getattr__(name):
101109
stacklevel=1,
102110
)
103111
return new_class
112+
if name in deprecated_exceptions:
113+
new_class = deprecated_exceptions[name]
114+
msg = f"{name} is deprecated, use {new_class.__name__} instead"
115+
warn(msg, DeprecationWarning, stacklevel=1)
116+
return new_class
104117
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
105118

106119

@@ -112,6 +125,11 @@ def __getattr__(name):
112125
SmartStrip = iot.IotStrip
113126
SmartDimmer = iot.IotDimmer
114127
SmartBulbPreset = BulbPreset
128+
129+
SmartDeviceException = KasaException
130+
UnsupportedDeviceException = UnsupportedDeviceError
131+
AuthenticationException = AuthenticationError
132+
TimeoutException = TimeoutError
115133
# Instanstiate all classes so the type checkers catch abstract issues
116134
from . import smart
117135

kasa/aestransport.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222
from .exceptions import (
2323
SMART_AUTHENTICATION_ERRORS,
2424
SMART_RETRYABLE_ERRORS,
25-
SMART_TIMEOUT_ERRORS,
26-
AuthenticationException,
27-
RetryableException,
28-
SmartDeviceException,
25+
AuthenticationError,
26+
DeviceError,
27+
KasaException,
2928
SmartErrorCode,
30-
TimeoutException,
29+
_RetryableError,
3130
)
3231
from .httpclient import HttpClient
3332
from .json import dumps as json_dumps
@@ -141,14 +140,12 @@ def _handle_response_error_code(self, resp_dict: Any, msg: str) -> None:
141140
if error_code == SmartErrorCode.SUCCESS:
142141
return
143142
msg = f"{msg}: {self._host}: {error_code.name}({error_code.value})"
144-
if error_code in SMART_TIMEOUT_ERRORS:
145-
raise TimeoutException(msg, error_code=error_code)
146143
if error_code in SMART_RETRYABLE_ERRORS:
147-
raise RetryableException(msg, error_code=error_code)
144+
raise _RetryableError(msg, error_code=error_code)
148145
if error_code in SMART_AUTHENTICATION_ERRORS:
149146
self._state = TransportState.HANDSHAKE_REQUIRED
150-
raise AuthenticationException(msg, error_code=error_code)
151-
raise SmartDeviceException(msg, error_code=error_code)
147+
raise AuthenticationError(msg, error_code=error_code)
148+
raise DeviceError(msg, error_code=error_code)
152149

153150
async def send_secure_passthrough(self, request: str) -> Dict[str, Any]:
154151
"""Send encrypted message as passthrough."""
@@ -171,7 +168,7 @@ async def send_secure_passthrough(self, request: str) -> Dict[str, Any]:
171168
# _LOGGER.debug(f"secure_passthrough response is {status_code}: {resp_dict}")
172169

173170
if status_code != 200:
174-
raise SmartDeviceException(
171+
raise KasaException(
175172
f"{self._host} responded with an unexpected "
176173
+ f"status code {status_code} to passthrough"
177174
)
@@ -197,7 +194,7 @@ async def send_secure_passthrough(self, request: str) -> Dict[str, Any]:
197194
self._host,
198195
)
199196
except Exception:
200-
raise SmartDeviceException(
197+
raise KasaException(
201198
f"Unable to decrypt response from {self._host}, "
202199
+ f"error: {ex}, response: {raw_response}",
203200
ex,
@@ -208,7 +205,7 @@ async def perform_login(self):
208205
"""Login to the device."""
209206
try:
210207
await self.try_login(self._login_params)
211-
except AuthenticationException as aex:
208+
except AuthenticationError as aex:
212209
try:
213210
if aex.error_code is not SmartErrorCode.LOGIN_ERROR:
214211
raise aex
@@ -223,10 +220,10 @@ async def perform_login(self):
223220
"%s: logged in with default credentials",
224221
self._host,
225222
)
226-
except AuthenticationException:
223+
except AuthenticationError:
227224
raise
228225
except Exception as ex:
229-
raise SmartDeviceException(
226+
raise KasaException(
230227
"Unable to login and trying default "
231228
+ f"login raised another exception: {ex}",
232229
ex,
@@ -292,7 +289,7 @@ async def perform_handshake(self) -> None:
292289
_LOGGER.debug("Device responded with: %s", resp_dict)
293290

294291
if status_code != 200:
295-
raise SmartDeviceException(
292+
raise KasaException(
296293
f"{self._host} responded with an unexpected "
297294
+ f"status code {status_code} to handshake"
298295
)
@@ -347,7 +344,7 @@ async def send(self, request: str) -> Dict[str, Any]:
347344
await self.perform_login()
348345
# After a login failure handshake needs to
349346
# be redone or a 9999 error is received.
350-
except AuthenticationException as ex:
347+
except AuthenticationError as ex:
351348
self._state = TransportState.HANDSHAKE_REQUIRED
352349
raise ex
353350

kasa/cli.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import asyncclick as click
1414

1515
from kasa import (
16-
AuthenticationException,
16+
AuthenticationError,
1717
Bulb,
1818
ConnectionType,
1919
Credentials,
@@ -22,8 +22,8 @@
2222
DeviceFamilyType,
2323
Discover,
2424
EncryptType,
25-
SmartDeviceException,
26-
UnsupportedDeviceException,
25+
KasaException,
26+
UnsupportedDeviceError,
2727
)
2828
from kasa.discover import DiscoveryResult
2929
from kasa.iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip
@@ -458,7 +458,7 @@ async def discover(ctx):
458458
unsupported = []
459459
auth_failed = []
460460

461-
async def print_unsupported(unsupported_exception: UnsupportedDeviceException):
461+
async def print_unsupported(unsupported_exception: UnsupportedDeviceError):
462462
unsupported.append(unsupported_exception)
463463
async with sem:
464464
if unsupported_exception.discovery_result:
@@ -476,7 +476,7 @@ async def print_discovered(dev: Device):
476476
async with sem:
477477
try:
478478
await dev.update()
479-
except AuthenticationException:
479+
except AuthenticationError:
480480
auth_failed.append(dev._discovery_info)
481481
echo("== Authentication failed for device ==")
482482
_echo_discovery_info(dev._discovery_info)
@@ -677,7 +677,7 @@ async def cmd_command(dev: Device, module, command, parameters):
677677
elif isinstance(dev, SmartDevice):
678678
res = await dev._query_helper(command, parameters)
679679
else:
680-
raise SmartDeviceException("Unexpected device type %s.", dev)
680+
raise KasaException("Unexpected device type %s.", dev)
681681
echo(json.dumps(res))
682682
return res
683683

kasa/device.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .device_type import DeviceType
1010
from .deviceconfig import DeviceConfig
1111
from .emeterstatus import EmeterStatus
12-
from .exceptions import SmartDeviceException
12+
from .exceptions import KasaException
1313
from .feature import Feature
1414
from .iotprotocol import IotProtocol
1515
from .protocol import BaseProtocol
@@ -242,12 +242,12 @@ def get_plug_by_name(self, name: str) -> "Device":
242242
if p.alias == name:
243243
return p
244244

245-
raise SmartDeviceException(f"Device has no child with {name}")
245+
raise KasaException(f"Device has no child with {name}")
246246

247247
def get_plug_by_index(self, index: int) -> "Device":
248248
"""Return child device for the given index."""
249249
if index + 1 > len(self.children< 8C97 /span>) or index < 0:
250-
raise SmartDeviceException(
250+
raise KasaException(
251251
f"Invalid index {index}, device has {len(self.children)} plugs"
252252
)
253253
return self.children[index]
@@ -306,7 +306,7 @@ def _add_feature(self, feature: Feature):
306306
"""Add a new feature to the device."""
307307
desc_name = feature.name.lower().replace(" ", "_")
308308
if desc_name in self._features:
309-
raise SmartDeviceException("Duplicate feature name %s" % desc_name)
309+
raise KasaException("Duplicate feature name %s" % desc_name)
310310
self._features[desc_name] = feature
311311

312312
@property

0 commit comments

Comments
 (0)
0