Skip to content

Commit da79100

Browse files
committed
linter fixes
1 parent 8fe3947 commit da79100

File tree

7 files changed

+99
-41
lines changed

7 files changed

+99
-41
lines changed

custom_components/openwrt/api/luci_rpc.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ async def _rpc_call(
167167
raise LuciRpcError(f"Communication error: {err}") from err
168168

169169
return data.get("result")
170+
170171
async def execute_command(self, command: str) -> str:
171172
"""Execute a command via LuCI RPC sys.exec."""
172173
try:
@@ -980,7 +981,6 @@ async def get_ip_neighbors(self) -> list[IpNeighbor]:
980981
_LOGGER.debug("Failed to get IP neighbors via LuCI RPC: %s", err)
981982
return neighbors
982983

983-
984984
async def reboot(self) -> bool:
985985
"""Reboot the device via LuCI RPC."""
986986
try:
@@ -993,8 +993,6 @@ async def reboot(self) -> bool:
993993
except Exception:
994994
return False
995995

996-
997-
998996
async def set_wireless_enabled(self, interface: str, enabled: bool) -> bool:
999997
"""Enable or disable a wireless radio via UCI."""
1000998
try:

custom_components/openwrt/api/ssh.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def _run() -> str:
120120
)
121121

122122
return ""
123+
123124
async def execute_command(self, command: str) -> str:
124125
"""Execute a command via SSH."""
125126
return await self._exec(command)
@@ -703,7 +704,6 @@ async def manage_service(self, name: str, action: str) -> bool:
703704
_LOGGER.error("Failed to %s service %s: %s", action, name, err)
704705
return False
705706

706-
707707
async def reboot(self) -> bool:
708708
"""Reboot the device."""
709709
try:
@@ -713,7 +713,6 @@ async def reboot(self) -> bool:
713713
_LOGGER.error("Failed to reboot: %s", err)
714714
return False
715715

716-
717716
async def set_wireless_enabled(self, interface: str, enabled: bool) -> bool:
718717
"""Enable/disable a wireless interface."""
719718
try:
@@ -726,7 +725,6 @@ async def set_wireless_enabled(self, interface: str, enabled: bool) -> bool:
726725
_LOGGER.error("Failed to set wireless %s: %s", interface, err)
727726
return False
728727

729-
730728
async def get_leds(self) -> list:
731729
"""Get LEDs from /sys/class/leds."""
732730
from .base import LedInfo

custom_components/openwrt/config_flow.py

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ async def _async_check_reachable(self, host: str, connection_type: str) -> bool:
238238
writer.close()
239239
await writer.wait_closed()
240240
return True
241-
except (TimeoutError, socket.gaierror, ConnectionRefusedError, OSError):
241+
except TimeoutError, socket.gaierror, ConnectionRefusedError, OSError:
242242
continue
243243

244244
return False
@@ -602,14 +602,24 @@ async def _test_connection(self, data: dict[str, Any]) -> str | None:
602602
_LOGGER.warning("API error during connection test: %s", err)
603603
return "cannot_connect"
604604
except Exception as err: # noqa: BLE001
605-
_LOGGER.exception("Unexpected error during connection test for %s: %s", data.get(CONF_USERNAME), err)
605+
_LOGGER.exception(
606+
"Unexpected error during connection test for %s: %s",
607+
data.get(CONF_USERNAME),
608+
err,
609+
)
606610
return "unknown"
607611

608612
async def async_step_provision_user(
609-
self, user_input: dict[str, Any] | None = None, errors: dict[str, str] | None = None
613+
self,
614+
user_input: dict[str, Any] | None = None,
615+
errors: dict[str, str] | None = None,
610616
) -> ConfigFlowResult:
611617
"""Step to ask if user wants to provision a dedicated user."""
612-
_LOGGER.info("Entering async_step_provision_user: input=%s, errors=%s", user_input, errors)
618+
_LOGGER.info(
619+
"Entering async_step_provision_user: input=%s, errors=%s",
620+
user_input,
621+
errors,
622+
)
613623
if user_input is not None:
614624
mode = user_input.get("mode")
615625
if mode == "create" or mode == "reset":
@@ -681,25 +691,38 @@ async def async_step_do_provision(self) -> ConfigFlowResult:
681691
await client.connect()
682692
# The provisioning script will restart services in background
683693
# it's possible the connection drops exactly when/after sending SUCCESS
684-
success = await client.provision_user("homeassistant", self._generated_password)
694+
success = await client.provision_user(
695+
"homeassistant", self._generated_password
696+
)
685697
if not success:
686698
self._provision_error = "Provisioning script returned failure. Check router logs (logread)."
687699
await client.disconnect()
688700
except TimeoutError:
689-
_LOGGER.warning("Provisioning timed out for %s. It might have succeeded if services are restarting.", self._data.get(CONF_HOST))
701+
_LOGGER.warning(
702+
"Provisioning timed out for %s. It might have succeeded if services are restarting.",
703+
self._data.get(CONF_HOST),
704+
)
690705
# We don't mark as success here, but if the script worked,
691706
# the next step (testing new user) might still work
692707
self._provision_error = "Timeout during provisioning. The router might be slow or restarting services."
693708
except Exception as err:
694709
err_msg = str(err).lower()
695710
# If we get a connection drop, it's highly likely service restarts triggered it
696-
if any(m in err_msg for m in ["connection reset", "broken pipe", "closed", "eof"]):
697-
_LOGGER.info("Connection dropped during provisioning for %s - this is expected during service restarts.", self._data.get(CONF_HOST))
711+
if any(
712+
m in err_msg
713+
for m in ["connection reset", "broken pipe", "closed", "eof"]
714+
):
715+
_LOGGER.info(
716+
"Connection dropped during provisioning for %s - this is expected during service restarts.",
717+
self._data.get(CONF_HOST),
718+
)
698719
# We assume success if the command was at least sent and no explicit error returned
699720
# The next step 'display_new_user' does a thorough re-connect test
700721
success = True
701722
else:
702-
_LOGGER.error("Provisioning failed for %s: %s", self._data.get(CONF_HOST), err)
723+
_LOGGER.error(
724+
"Provisioning failed for %s: %s", self._data.get(CONF_HOST), err
725+
)
703726
self._provision_error = str(err)
704727

705728
if success:
@@ -712,7 +735,9 @@ async def async_step_do_provision(self) -> ConfigFlowResult:
712735
return self.async_show_form(
713736
step_id="provision_failed",
714737
errors={"base": "provision_failed"},
715-
description_placeholders={"error": self._provision_error or "Unknown error"},
738+
description_placeholders={
739+
"error": self._provision_error or "Unknown error"
740+
},
716741
)
717742

718743
async def async_step_provision_failed(
@@ -733,26 +758,33 @@ async def async_step_display_new_user(
733758
# Wait for services to fully restart after provisioning
734759
# Slower devices need more time for rpcd to come back up
735760
# We wait 10s now initially as it's a critical phase
736-
_LOGGER.info("Provisioning finished. Waiting 10s for router services to restart...")
761+
_LOGGER.info(
762+
"Provisioning finished. Waiting 10s for router services to restart..."
763+
)
737764
await asyncio.sleep(10)
738765

739766
# Re-check permissions with new user with retries
740767
new_user_success = False
741768
for attempt in range(10):
742769
_LOGGER.info(
743770
"Testing connection with new user 'homeassistant' (attempt %s/10)",
744-
attempt + 1
771+
attempt + 1,
745772
)
746773
# Use a fresh connection test to avoid session leakage
747774
error = await self._test_connection(self._data)
748775
if not error:
749-
_LOGGER.info("Connection with new user successful on attempt %s", attempt + 1)
776+
_LOGGER.info(
777+
"Connection with new user successful on attempt %s",
778+
attempt + 1,
779+
)
750780
new_user_success = True
751781
break
752782

753783
_LOGGER.warning(
754784
"Auth attempt %s failed for %s: %s. Router might still be restarting services. Waiting 5s...",
755-
attempt + 1, self._data.get(CONF_HOST), error
785+
attempt + 1,
786+
self._data.get(CONF_HOST),
787+
error,
756788
)
757789
await asyncio.sleep(5)
758790

@@ -762,7 +794,7 @@ async def async_step_display_new_user(
762794
"Config might have applied but services didn't pick it up or user creation failed. "
763795
"Check your router logs for 'ha-openwrt' tags. Last error: %s",
764796
self._data.get(CONF_HOST),
765-
error
797+
error,
766798
)
767799
return await self.async_step_provision_user(
768800
errors={"base": error or "invalid_auth"}

tests/test_api_luci_rpc.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ async def test_luci_set_sqm_config(luci_client: LuciRpcClient):
132132
async def test_luci_provision_user(luci_client: LuciRpcClient):
133133
"""Test user provisioning via LuCI RPC."""
134134
luci_client._auth_token = "luci_test_token"
135-
with patch.object(luci_client, "execute_command", new_callable=AsyncMock) as mock_exec:
135+
with patch.object(
136+
luci_client, "execute_command", new_callable=AsyncMock
137+
) as mock_exec:
136138
mock_exec.return_value = "LOG: Provisioning SUCCESS"
137139

138140
success = await luci_client.provision_user("homeassistant", "new-password")
@@ -141,6 +143,6 @@ async def test_luci_provision_user(luci_client: LuciRpcClient):
141143
script = mock_exec.call_args[0][0]
142144
assert "USER=$(cat <<'EOF'\nhomeassistant\nEOF\n)" in script
143145
assert "PASS=$(cat <<'EOF'\nnew-password\nEOF\n)" in script
144-
assert 'uci set rpcd.homeassistant=login' in script
146+
assert "uci set rpcd.homeassistant=login" in script
145147
assert 'uci set rpcd.homeassistant.password="\\$p\\$$USER"' in script
146-
assert '/etc/init.d/rpcd restart' in script
148+
assert "/etc/init.d/rpcd restart" in script

tests/test_api_ssh.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ async def test_ssh_provision_user(ssh_client: SshClient):
166166
script = mock_exec.call_args[0][0]
167167
assert "USER=$(cat <<'EOF'\nhomeassistant\nEOF\n)" in script
168168
assert "PASS=$(cat <<'EOF'\nnew-password\nEOF\n)" in script
169-
assert 'uci set rpcd.homeassistant=login' in script
169+
assert "uci set rpcd.homeassistant=login" in script
170170
assert 'uci set rpcd.homeassistant.password="\\$p\\$$USER"' in script
171171
assert 'uci add_list rpcd.homeassistant.read="homeassistant"' in script
172-
assert 'chpasswd' in script
173-
assert 'passwd' in script
174-
assert '/etc/init.d/rpcd restart' in script
172+
assert "chpasswd" in script
173+
assert "passwd" in script
174+
assert "/etc/init.d/rpcd restart" in script

tests/test_api_ubus.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ async def test_ubus_check_permissions_root(ubus_client: UbusClient):
181181
async def test_ubus_provision_user(ubus_client: UbusClient):
182182
"""Test user provisioning via ubus."""
183183
ubus_client._session_id = "test_token"
184-
with patch.object(ubus_client, "execute_command", new_callable=AsyncMock) as mock_exec:
184+
with patch.object(
185+
ubus_client, "execute_command", new_callable=AsyncMock
186+
) as mock_exec:
185187
mock_exec.return_value = "LOG: Provisioning SUCCESS"
186188

187189
success = await ubus_client.provision_user("homeassistant", "new-password")
@@ -190,6 +192,6 @@ async def test_ubus_provision_user(ubus_client: UbusClient):
190192
script = mock_exec.call_args[0][0]
191193
assert "USER=$(cat <<'EOF'\nhomeassistant\nEOF\n)" in script
192194
assert "PASS=$(cat <<'EOF'\nnew-password\nEOF\n)" in script
193-
assert 'uci set rpcd.homeassistant=login' in script
195+
assert "uci set rpcd.homeassistant=login" in script
194196
assert 'uci set rpcd.homeassistant.password="\\$p\\$$USER"' in script
195-
assert '/etc/init.d/rpcd restart' in script
197+
assert "/etc/init.d/rpcd restart" in script

tests/test_translations.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,17 @@ def extract_data_keys(data, path=""):
134134
if var in const_map:
135135
value = const_map[var]
136136
# Exclude known internals or basic ones that are sometimes omitted or handled differently
137-
if value in ["host", "connection_type", "username", "password", "port", "use_ssl", "verify_ssl", "ubus_path", "ssh_key"]:
137+
if value in [
138+
"host",
139+
"connection_type",
140+
"username",
141+
"password",
142+
"port",
143+
"use_ssl",
144+
"verify_ssl",
145+
"ubus_path",
146+
"ssh_key",
147+
]:
138148
continue
139149

140150
# If the value is used in code, it SHOULD be in a .data block somewhere
@@ -162,27 +172,43 @@ def test_steps_and_errors_translated():
162172

163173
# 1. Check step_id="..."
164174
found_steps = set(re.findall(r'step_id=["\']([^"\']+)["\']', content))
165-
translated_steps = set(config_data.get("step", {}).keys()) | set(options_data.get("step", {}).keys())
175+
translated_steps = set(config_data.get("step", {}).keys()) | set(
176+
options_data.get("step", {}).keys()
177+
)
166178

167179
missing_steps = found_steps - translated_steps
168-
assert not missing_steps, f"Steps used in code but missing in strings.json: {sorted(missing_steps)}"
180+
assert not missing_steps, (
181+
f"Steps used in code but missing in strings.json: {sorted(missing_steps)}"
182+
)
169183

170184
# 2. Check errors={"base": "...", ...} or errors={"field": "..."}
171-
errors_sec = re.findall(r'errors\s*=\s*\{([^}]+)\}', content)
185+
errors_sec = re.findall(r"errors\s*=\s*\{([^}]+)\}", content)
172186
found_error_keys = set()
173187
for sec in errors_sec:
174188
found_error_keys.update(re.findall(r':\s*["\']([^"\']+)["\']', sec))
175189

176-
translated_errors = set(config_data.get("error", {}).keys()) | set(options_data.get("error", {}).keys())
190+
translated_errors = set(config_data.get("error", {}).keys()) | set(
191+
options_data.get("error", {}).keys()
192+
)
177193
missing_errors = found_error_keys - translated_errors - found_steps
178194

179195
# Static exclusion for known dynamic or placeholder keys
180-
missing_errors = {e for e in missing_errors if not e.startswith("{") and e not in ["base"]}
196+
missing_errors = {
197+
e for e in missing_errors if not e.startswith("{") and e not in ["base"]
198+
}
181199

182-
assert not missing_errors, f"Error keys used in code but missing in strings.json: {sorted(missing_errors)}"
200+
assert not missing_errors, (
201+
f"Error keys used in code but missing in strings.json: {sorted(missing_errors)}"
202+
)
183203

184204
# 3. Check async_abort(reason="...")
185-
found_abort_reasons = set(re.findall(r'async_abort\(reason=["\']([^"\']+)["\']', content))
186-
translated_aborts = set(config_data.get("abort", {}).keys()) | set(options_data.get("abort", {}).keys())
205+
found_abort_reasons = set(
206+
re.findall(r'async_abort\(reason=["\']([^"\']+)["\']', content)
207+
)
208+
translated_aborts = set(config_data.get("abort", {}).keys()) | set(
209+
options_data.get("abort", {}).keys()
210+
)
187211
missing_aborts = found_abort_reasons - translated_aborts
188-
assert not missing_aborts, f"Abort reasons used in code but missing in strings.json: {sorted(missing_aborts)}"
212+
assert not missing_aborts, (
213+
f"Abort reasons used in code but missing in strings.json: {sorted(missing_aborts)}"
214+
)

0 commit comments

Comments
 (0)