From 2523a75ddf8f36884dfee81778aa96e4195569bd Mon Sep 17 00:00:00 2001 From: Mark Parker <34688010+MarkParker5@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:47:49 +0200 Subject: [PATCH] Drop MissingCredentialsError.parse --- majordom_integration_sdk/errors.py | 20 ++++++-------------- tests/test_errors.py | 14 +------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/majordom_integration_sdk/errors.py b/majordom_integration_sdk/errors.py index af756b1..39cc6e6 100644 --- a/majordom_integration_sdk/errors.py +++ b/majordom_integration_sdk/errors.py @@ -1,18 +1,18 @@ """Errors an integration raises for the Hub (and through it the app) to act on.""" import re -from typing import ClassVar, Final, Self +from typing import ClassVar _TYPE = re.compile(r"[a-z][a-z0-9]*(?:_[a-z0-9]+)*") -_PREFIX: Final = "Missing credentials: " class MissingCredentialsError(LookupError): """The Hub lacks credentials of ``type`` to pair a device, e.g. a Hub on Ethernet has no Wi-Fi to give a node. - Raised before the device is touched, with ``str(error)`` as the discovery's ``last_error``, so the app can ask the - user for them and pair again. ``type`` is a lower_snake_case string kept open for integrations: the known ones are - the class constants below; an integration documents any custom one it raises. The app maps it to an enum. + Raised before the device is touched; the Hub answers the pairing request with it (422 ``missing_credentials``, + carrying ``type`` and the kept device id), so the app can ask the user for them and pair again. ``type`` is a + lower_snake_case string kept open for integrations: the known ones are the class constants below; an integration + documents any custom one it raises. The app maps it to an enum. """ WIFI: ClassVar[str] = "wifi" @@ -22,12 +22,4 @@ def __init__(self, type: str = WIFI) -> None: if not _TYPE.fullmatch(type): raise ValueError(f"Credentials type must be lower_snake_case, got {type!r}") self.type = type - super().__init__(f"{_PREFIX}{type}") - - @classmethod - def parse(cls, last_error: str | None) -> Self | None: - """The error a ``last_error`` was set from, or None if it was set from anything else.""" - if last_error is None or not last_error.startswith(_PREFIX): - return None - type_ = last_error.removeprefix(_PREFIX) - return cls(type_) if _TYPE.fullmatch(type_) else None + super().__init__(f"Missing credentials: {type}") diff --git a/tests/test_errors.py b/tests/test_errors.py index 6fbae73..742508a 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -1,4 +1,4 @@ -"""MissingCredentialsError: what the Hub lacks to pair a device, readable back from a last_error.""" +"""MissingCredentialsError: what the Hub lacks to pair a device.""" import pytest @@ -29,15 +29,3 @@ def test_the_type_is_lower_snake_case(type_: str): def test_the_hub_already_handles_it_as_a_lookup_error(): assert issubclass(MissingCredentialsError, LookupError) - - -@pytest.mark.parametrize("type_", ["wifi", "thread", "zigbee_network_key"]) -def test_the_type_is_read_back_from_a_last_error(type_: str): - parsed = MissingCredentialsError.parse(str(MissingCredentialsError(type_))) - assert parsed is not None - assert parsed.type == type_ - - -@pytest.mark.parametrize("last_error", [None, "", "Device is no longer connected", "Missing credentials: Wi-Fi"]) -def test_other_errors_are_not_read_as_missing_credentials(last_error: str | None): - assert MissingCredentialsError.parse(last_error) is None