GeezerVison is showing github.com — Refactor exception classes · python-kasa/python-kasa@277ac5d
File treeExpand file treeCollapse file tree Open diff view settings Some content is hiddenLarge Commits have some content hidden by default. Use the searchbox below for content that may be hidden. Dismiss bannerExpand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: devtools/dump_devinfo.py +7-10Lines changed: 7 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 22 | 22 |
| 23 | 23 | from devtools.helpers.smartrequests import SmartRequest, get_component_requests | 24 | 24 | from kasa import ( | 25 | | - AuthenticationException, | | 25 | + AuthenticationError, | 26 | 26 | Credentials, | 27 | 27 | Device, | 28 | 28 | Discover, | 29 | | - SmartDeviceException, | 30 | | - TimeoutException, | | 29 | + KasaException, | | 30 | + TimeoutError, | 31 | 31 | ) | 32 | 32 | from kasa.discover import DiscoveryResult | 33 | 33 | from kasa.exceptions import SmartErrorCode | @@ -303,19 +303,16 @@ async def _make_requests_or_exit( | 303 | 303 | for method, result in responses.items(): | 304 | 304 | final[method] = result | 305 | 305 | return final | 306 | | - except AuthenticationException as ex: | | 306 | + except AuthenticationError as ex: | 307 | 307 | _echo_error( | 308 | 308 | f"Unable to query the device due to an authentication error: {ex}", | 309 | 309 | ) | 310 | 310 | exit(1) | 311 | | - except SmartDeviceException as ex: | | 311 | + except KasaException as ex: | 312 | 312 | _echo_error( | 313 | 313 | f"Unable to query {name} at once: {ex}", | 314 | 314 | ) | 315 | | - if ( | 316 | | - isinstance(ex, TimeoutException) | 317 | | - or ex.error_code == SmartErrorCode.SESSION_TIMEOUT_ERROR | 318 | | - ): | | 315 | + if isinstance(ex, TimeoutError): | 319 | 316 | _echo_error( | 320 | 317 | "Timeout, try reducing the batch size via --batch-size option.", | 321 | 318 | ) | @@ -400,7 +397,7 @@ async def get_smart_fixture(device: SmartDevice, batch_size: int): | 400 | 397 | response = await device.protocol.query( | 401 | 398 | SmartRequest._create_request_dict(test_call.request) | 402 | 399 | ) | 403 | | - except AuthenticationException as ex: | | 400 | + except AuthenticationError as ex: | 404 | 401 | _echo_error( | 405 | 402 | f"Unable to query the device due to an authentication error: {ex}", | 406 | 403 | ) | |
Collapse file Copy file name to clipboardExpand all lines: kasa/__init__.py +27-9Lines changed: 27 additions & 9 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 8 | 8 | For device type specific actions `SmartBulb`, `SmartPlug`, or `SmartStrip` | 9 | 9 | should be used instead. | 10 | 10 |
| 11 | | -Module-specific errors are raised as `SmartDeviceException` and are expected | | 11 | +Module-specific errors are raised as `KasaException` and are expected | 12 | 12 | to be handled by the user of the library. | 13 | 13 | """ | 14 | 14 | from importlib.metadata import version | | 28 | 28 | from kasa.discover import Discover | 29 | 29 | from kasa.emeterstatus import EmeterStatus | 30 | 30 | from kasa.exceptions import ( | 31 | | - AuthenticationException, | 32 | | - SmartDeviceException, | 33 | | - TimeoutException, | 34 | | - UnsupportedDeviceException, | | 31 | + AuthenticationError, | | 32 | + DeviceError, | | 33 | + KasaException, | | 34 | + TimeoutError, | | 35 | + UnsupportedDeviceError, | 35 | 36 | ) | 36 | 37 | from kasa.feature import Feature, FeatureType | 37 | 38 | from kasa.iot.iotbulb import BulbPreset, TurnOnBehavior, TurnOnBehaviors | | 61 | 62 | "Device", | 62 | 63 | "Bulb", | 63 | 64 | "Plug", | 64 | | - "SmartDeviceException", | 65 | | - "AuthenticationException", | 66 | | - "UnsupportedDeviceException", | 67 | | - "TimeoutException", | | 65 | + "KasaException", | | 66 | + "AuthenticationError", | | 67 | + "DeviceError", | | 68 | + "UnsupportedDeviceError", | | 69 | + "TimeoutError", | 68 | 70 | "Credentials", | 69 | 71 | "DeviceConfig", | 70 | 72 | "ConnectionType", | | 84 | 86 | "SmartDimmer": iot.IotDimmer, | 85 | 87 | "SmartBulbPreset": BulbPreset, | 86 | 88 | } | | 89 | +deprecated_exceptions = { | | 90 | + "SmartDeviceException": KasaException, | | 91 | + "UnsupportedDeviceException": UnsupportedDeviceError, | | 92 | + "AuthenticationException": AuthenticationError, | | 93 | + "TimeoutException": TimeoutError, | | 94 | +} | 87 | 95 |
| 88 | 96 |
| 89 | 97 | def __getattr__(name): | @@ -101,6 +109,11 @@ def __getattr__(name): | 101 | 109 | stacklevel=1, | 102 | 110 | ) | 103 | 111 | 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 | 104 | 117 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") | 105 | 118 |
| 106 | 119 |
| @@ -112,6 +125,11 @@ def __getattr__(name): | 112 | 125 | SmartStrip = iot.IotStrip | 113 | 126 | SmartDimmer = iot.IotDimmer | 114 | 127 | SmartBulbPreset = BulbPreset | | 128 | + | | 129 | + SmartDeviceException = KasaException | | 130 | + UnsupportedDeviceException = UnsupportedDeviceError | | 131 | + AuthenticationException = AuthenticationError | | 132 | + TimeoutException = TimeoutError | 115 | 133 | # Instanstiate all classes so the type checkers catch abstract issues | 116 | 134 | from . import smart | 117 | 135 |
| |
Collapse file Copy file name to clipboardExpand all lines: kasa/aestransport.py +14-17Lines changed: 14 additions & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 22 | 22 | from .exceptions import ( | 23 | 23 | SMART_AUTHENTICATION_ERRORS, | 24 | 24 | SMART_RETRYABLE_ERRORS, | 25 | | - SMART_TIMEOUT_ERRORS, | 26 | | - AuthenticationException, | 27 | | - RetryableException, | 28 | | - SmartDeviceException, | | 25 | + AuthenticationError, | | 26 | + DeviceError, | | 27 | + KasaException, | 29 | 28 | SmartErrorCode, | 30 | | - TimeoutException, | | 29 | + _RetryableError, | 31 | 30 | ) | 32 | 31 | from .httpclient import HttpClient | 33 | 32 | from .json import dumps as json_dumps | @@ -141,14 +140,12 @@ def _handle_response_error_code(self, resp_dict: Any, msg: str) -> None: | 141 | 140 | if error_code == SmartErrorCode.SUCCESS: | 142 | 141 | return | 143 | 142 | 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) | 146 | 143 | if error_code in SMART_RETRYABLE_ERRORS: | 147 | | - raise RetryableException(msg, error_code=error_code) | | 144 | + raise _RetryableError(msg, error_code=error_code) | 148 | 145 | if error_code in SMART_AUTHENTICATION_ERRORS: | 149 | 146 | 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) | 152 | 149 |
| 153 | 150 | async def send_secure_passthrough(self, request: str) -> Dict[str, Any]: | 154 | 151 | """Send encrypted message as passthrough.""" | @@ -171,7 +168,7 @@ async def send_secure_passthrough(self, request: str) -> Dict[str, Any]: | 171 | 168 | # _LOGGER.debug(f"secure_passthrough response is {status_code}: {resp_dict}") | 172 | 169 |
| 173 | 170 | if status_code != 200: | 174 | | - raise SmartDeviceException( | | 171 | + raise KasaException( | 175 | 172 | f"{self._host} responded with an unexpected " | 176 | 173 | + f"status code {status_code} to passthrough" | 177 | 174 | ) | @@ -197,7 +194,7 @@ async def send_secure_passthrough(self, request: str) -> Dict[str, Any]: | 197 | 194 | self._host, | 198 | 195 | ) | 199 | 196 | except Exception: | 200 | | - raise SmartDeviceException( | | 197 | + raise KasaException( | 201 | 198 | f"Unable to decrypt response from {self._host}, " | 202 | 199 | + f"error: {ex}, response: {raw_response}", | 203 | 200 | ex, | @@ -208,7 +205,7 @@ async def perform_login(self): | 208 | 205 | """Login to the device.""" | 209 | 206 | try: | 210 | 207 | await self.try_login(self._login_params) | 211 | | - except AuthenticationException as aex: | | 208 | + except AuthenticationError as aex: | 212 | 209 | try: | 213 | 210 | if aex.error_code is not SmartErrorCode.LOGIN_ERROR: | 214 | 211 | raise aex | @@ -223,10 +220,10 @@ async def perform_login(self): | 223 | 220 | "%s: logged in with default credentials", | 224 | 221 | self._host, | 225 | 222 | ) | 226 | | - except AuthenticationException: | | 223 | + except AuthenticationError: | 227 | 224 | raise | 228 | 225 | except Exception as ex: | 229 | | - raise SmartDeviceException( | | 226 | + raise KasaException( | 230 | 227 | "Unable to login and trying default " | 231 | 228 | + f"login raised another exception: {ex}", | 232 | 229 | ex, | @@ -292,7 +289,7 @@ async def perform_handshake(self) -> None: | 292 | 289 | _LOGGER.debug("Device responded with: %s", resp_dict) | 293 | 290 |
| 294 | 291 | if status_code != 200: | 295 | | - raise SmartDeviceException( | | 292 | + raise KasaException( | 296 | 293 | f"{self._host} responded with an unexpected " | 297 | 294 | + f"status code {status_code} to handshake" | 298 | 295 | ) | @@ -347,7 +344,7 @@ async def send(self, request: str) -> Dict[str, Any]: | 347 | 344 | await self.perform_login() | 348 | 345 | # After a login failure handshake needs to | 349 | 346 | # be redone or a 9999 error is received. | 350 | | - except AuthenticationException as ex: | | 347 | + except AuthenticationError as ex: | 351 | 348 | self._state = TransportState.HANDSHAKE_REQUIRED | 352 | 349 | raise ex | 353 | 350 |
| |
Collapse file Copy file name to clipboardExpand all lines: kasa/cli.py +6-6Lines changed: 6 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 13 | 13 | import asyncclick as click | 14 | 14 |
| 15 | 15 | from kasa import ( | 16 | | - AuthenticationException, | | 16 | + AuthenticationError, | 17 | 17 | Bulb, | 18 | 18 | ConnectionType, | 19 | 19 | Credentials, | | 22 | 22 | DeviceFamilyType, | 23 | 23 | Discover, | 24 | 24 | EncryptType, | 25 | | - SmartDeviceException, | 26 | | - UnsupportedDeviceException, | | 25 | + KasaException, | | 26 | + UnsupportedDeviceError, | 27 | 27 | ) | 28 | 28 | from kasa.discover import DiscoveryResult | 29 | 29 | from kasa.iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip | @@ -458,7 +458,7 @@ async def discover(ctx): | 458 | 458 | unsupported = [] | 459 | 459 | auth_failed = [] | 460 | 460 |
| 461 | | - async def print_unsupported(unsupported_exception: UnsupportedDeviceException): | | 461 | + async def print_unsupported(unsupported_exception: UnsupportedDeviceError): | 462 | 462 | unsupported.append(unsupported_exception) | 463 | 463 | async with sem: | 464 | 464 | if unsupported_exception.discovery_result: | @@ -476,7 +476,7 @@ async def print_discovered(dev: Device): | 476 | 476 | async with sem: | 477 | 477 | try: | 478 | 478 | await dev.update() | 479 | | - except AuthenticationException: | | 479 | + except AuthenticationError: | 480 | 480 | auth_failed.append(dev._discovery_info) | 481 | 481 | echo("== Authentication failed for device ==") | 482 | 482 | _echo_discovery_info(dev._discovery_info) | @@ -677,7 +677,7 @@ async def cmd_command(dev: Device, module, command, parameters): | 677 | 677 | elif isinstance(dev, SmartDevice): | 678 | 678 | res = await dev._query_helper(command, parameters) | 679 | 679 | else: | 680 | | - raise SmartDeviceException("Unexpected device type %s.", dev) | | 680 | + raise KasaException("Unexpected device type %s.", dev) | 681 | 681 | echo(json.dumps(res)) | 682 | 682 | return res | 683 | 683 |
| |
Collapse file Copy file name to clipboardExpand all lines: kasa/device.py +4-4Lines changed: 4 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 9 | 9 | from .device_type import DeviceType | 10 | 10 | from .deviceconfig import DeviceConfig | 11 | 11 | from .emeterstatus import EmeterStatus | 12 | | -from .exceptions import SmartDeviceException | | 12 | +from .exceptions import KasaException | 13 | 13 | from .feature import Feature | 14 | 14 | from .iotprotocol import IotProtocol | 15 | 15 | from .protocol import BaseProtocol | @@ -242,12 +242,12 @@ def get_plug_by_name(self, name: str) -> "Device": | 242 | 242 | if p.alias == name: | 243 | 243 | return p | 244 | 244 |
| 245 | | - raise SmartDeviceException(f"Device has no child with {name}") | | 245 | + raise KasaException(f"Device has no child with {name}") | 246 | 246 |
| 247 | 247 | def get_plug_by_index(self, index: int) -> "Device": | 248 | 248 | """Return child device for the given index.""" | 249 | 249 | if index + 1 > len(self.children) or index < 0: | 250 | | - raise SmartDeviceException( | | 250 | + raise KasaException( | 251 | 251 | f"Invalid index {index}, device has {len(self.children)} plugs" | 252 | 252 | ) | 253 | 253 | return self.children[index] | @@ -306,7 +306,7 @@ def _add_feature(self, feature: Feature): | 306 | 306 | """Add a new feature to the device.""" | 307 | 307 | desc_name = feature.name.lower().replace(" ", "_") | 308 | 308 | if desc_name in self._features: | 309 | | - raise SmartDeviceException("Duplicate feature name %s" % desc_name) | | 309 | + raise KasaException("Duplicate feature name %s" % desc_name) | 310 | 310 | self._features[desc_name] = feature | 311 | 311 |
| 312 | 312 | @property | |
Collapse file Copy file name to clipboardExpand all lines: kasa/device_factory.py +7-7Lines changed: 7 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 6 | 6 | from .aestransport import AesTransport | 7 | 7 | from .device import Device | 8 | 8 | from .deviceconfig import DeviceConfig | 9 | | -from .exceptions import SmartDeviceException, UnsupportedDeviceException | | 9 | +from .exceptions import KasaException, UnsupportedDeviceError | 10 | 10 | from .iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip | 11 | 11 | from .iotprotocol import IotProtocol | 12 | 12 | from .klaptransport import KlapTransport, KlapTransportV2 | @@ -45,12 +45,12 @@ async def connect(*, host: Optional[str] = None, config: DeviceConfig) -> "Devic | 45 | 45 | :return: Object for querying/controlling found device. | 46 | 46 | """ | 47 | 47 | if host and config or (not host and not config): | 48 | | - raise SmartDeviceException("One of host or config must be provded and not both") | | 48 | + raise KasaException("One of host or config must be provded and not both") | 49 | 49 | if host: | 50 | 50 | config = DeviceConfig(host=host) | 51 | 51 |
| 52 | 52 | if (protocol := get_protocol(config=config)) is None: | 53 | | - raise UnsupportedDeviceException( | | 53 | + raise UnsupportedDeviceError( | 54 | 54 | f"Unsupported device for {config.host}: " | 55 | 55 | + f"{config.connection_type.device_family.value}" | 56 | 56 | ) | @@ -99,7 +99,7 @@ def _perf_log(has_params, perf_type): | 99 | 99 | _perf_log(True, "update") | 100 | 100 | return device | 101 | 101 | else: | 102 | | - raise UnsupportedDeviceException( | | 102 | + raise UnsupportedDeviceError( | 103 | 103 | f"Unsupported device for {config.host}: " | 104 | 104 | + f"{config.connection_type.device_family.value}" | 105 | 105 | ) | @@ -108,12 +108,12 @@ def _perf_log(has_params, perf_type): | 108 | 108 | def get_device_class_from_sys_info(info: Dict[str, Any]) -> Type[IotDevice]: | 109 | 109 | """Find SmartDevice subclass for device described by passed data.""" | 110 | 110 | if "system" not in info or "get_sysinfo" not in info["system"]: | 111 | | - raise SmartDeviceException("No 'system' or 'get_sysinfo' in response") | | 111 | + raise KasaException("No 'system' or 'get_sysinfo' in response") | 112 | 112 |
| 113 | 113 | sysinfo: Dict[str, Any] = info["system"]["get_sysinfo"] | 114 | 114 | type_: Optional[str] = sysinfo.get("type", sysinfo.get("mic_type")) | 115 | 115 | if type_ is None: | 116 | | - raise SmartDeviceException("Unable to find the device type field!") | | 116 | + raise KasaException("Unable to find the device type field!") | 117 | 117 |
| 118 | 118 | if "dev_name" in sysinfo and "Dimmer" in sysinfo["dev_name"]: | 119 | 119 | return IotDimmer | @@ -129,7 +129,7 @@ def get_device_class_from_sys_info(info: Dict[str, Any]) -> Type[IotDevice]: | 129 | 129 | return IotLightStrip | 130 | 130 |
| 131 | 131 | return IotBulb | 132 | | - raise UnsupportedDeviceException("Unknown device type: %s" % type_) | | 132 | + raise UnsupportedDeviceError("Unknown device type: %s" % type_) | 133 | 133 |
| 134 | 134 |
| 135 | 135 | def get_device_class_from_family(device_type: str) -> Optional[Type[Device]]: | |
Collapse file Copy file name to clipboardExpand all lines: kasa/deviceconfig.py +5-7Lines changed: 5 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 5 | 5 | from typing import TYPE_CHECKING, Dict, Optional, Union | 6 | 6 |
| 7 | 7 | from .credentials import Credentials | 8 | | -from .exceptions import SmartDeviceException | | 8 | +from .exceptions import KasaException | 9 | 9 |
| 10 | 10 | if TYPE_CHECKING: | 11 | 11 | from aiohttp import ClientSession | @@ -46,7 +46,7 @@ def _dataclass_from_dict(klass, in_val): | 46 | 46 | fieldtypes[dict_key], in_val[dict_key] | 47 | 47 | ) | 48 | 48 | else: | 49 | | - raise SmartDeviceException( | | 49 | + raise KasaException( | 50 | 50 | f"Cannot create dataclass from dict, unknown key: {dict_key}" | 51 | 51 | ) | 52 | 52 | return klass(**val) | @@ -92,7 +92,7 @@ def from_values( | 92 | 92 | login_version, | 93 | 93 | ) | 94 | 94 | except (ValueError, TypeError) as ex: | 95 | | - raise SmartDeviceException( | | 95 | + raise KasaException( | 96 | 96 | f"Invalid connection parameters for {device_family}." | 97 | 97 | + f"{encryption_type}.{login_version}" | 98 | 98 | ) from ex | @@ -113,9 +113,7 @@ def from_dict(connection_type_dict: Dict[str, str]) -> "ConnectionType": | 113 | 113 | login_version, # type: ignore[arg-type] | 114 | 114 | ) | 115 | 115 |
| 116 | | - raise SmartDeviceException( | 117 | | - f"Invalid connection type data for {connection_type_dict}" | 118 | | - ) | | 116 | + raise KasaException(f"Invalid connection type data for {connection_type_dict}") | 119 | 117 |
| 120 | 118 | def to_dict(self) -> Dict[str, Union[str, int]]: | 121 | 119 | """Convert connection params to dict.""" | @@ -185,4 +183,4 @@ def from_dict(config_dict: Dict[str, Dict[str, str]]) -> "DeviceConfig": | 185 | 183 | """Return device config from dict.""" | 186 | 184 | if isinstance(config_dict, dict): | 187 | 185 | return _dataclass_from_dict(DeviceConfig, config_dict) | 188 | | - raise SmartDeviceException(f"Invalid device config data: {config_dict}") | | 186 | + raise KasaException(f"Invalid device config data: {config_dict}") |
|