Skip to content

Commit 496daf4

Browse files
authored
Tag/improve quality (#71)
Shorter/cleaner error logging and suppressed exception chaining in discovery paths for clearer output. Reordered and tightened tests, improved expectations and parsing coverage, and adjusted test imports. Added lint suppressions and reorganized imports across scripts and tests.
1 parent 2ca459c commit 496daf4

19 files changed

+744
-478
lines changed

.github/workflows/verify.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ jobs:
9696
- name: Run all tests
9797
run: |
9898
. venv/bin/activate
99-
pytest --log-level info tests/*.py --cov='.'
99+
pytest --log-level info tests/ --cov='airos/'
100100
- name: Upload coverage artifact
101101
uses: actions/upload-artifact@v4
102102
with:

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ repos:
9292
pass_filenames: false
9393
- id: pytest
9494
name: "pytest"
95-
entry: script/run-in-env.sh pytest
95+
entry: script/run-in-env.sh pytest --log-level info tests/ --cov='airos/'
9696
language: script
9797
types: [python]
9898
pass_filenames: false

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.4.2] - 2025-08-17
6+
7+
### Changed
8+
9+
- Aligned quality targets either improved or tagged
10+
511
## [0.4.1] - 2025-08-17
612

713
### Changed

airos/airos8.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ async def _request_json(
217217
raise AirOSConnectionAuthenticationError from err
218218
raise AirOSConnectionSetupError from err
219219
except (TimeoutError, aiohttp.ClientError) as err:
220-
_LOGGER.exception("Error during API call to %s: %s", url, err)
220+
_LOGGER.exception("Error during API call to %s", url)
221221
raise AirOSDeviceConnectionError from err
222222
except json.JSONDecodeError as err:
223223
_LOGGER.error("Failed to decode JSON from %s", url)
@@ -242,8 +242,7 @@ async def status(self) -> AirOSData:
242242

243243
try:
244244
adjusted_json = self.derived_data(response)
245-
airos_data = AirOSData.from_dict(adjusted_json)
246-
return airos_data
245+
return AirOSData.from_dict(adjusted_json)
247246
except InvalidFieldValue as err:
248247
# Log with .error() as this is a specific, known type of issue
249248
redacted_data = redact_data_smart(response)

airos/data.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ def is_ip_address(value: str) -> bool:
3434
"""Check if a string is a valid IPv4 or IPv6 address."""
3535
try:
3636
ipaddress.ip_address(value)
37-
return True
3837
except ValueError:
3938
return False
39+
return True
4040

4141

4242
def redact_data_smart(data: dict[str, Any]) -> dict[str, Any]:
@@ -107,8 +107,6 @@ def _redact(d: dict[str, Any]) -> dict[str, Any]:
107107
class AirOSDataClass(DataClassDictMixin):
108108
"""A base class for all mashumaro dataclasses."""
109109

110-
pass
111-
112110

113111
def _check_and_log_unknown_enum_value(
114112
data_dict: dict[str, Any],
@@ -534,15 +532,11 @@ class Interface(AirOSDataClass):
534532
class ProvisioningMode(AirOSDataClass):
535533
"""Leaf definition."""
536534

537-
pass
538-
539535

540536
@dataclass
541537
class NtpClient(AirOSDataClass):
542538
"""Leaf definition."""
543539

544-
pass
545-
546540

547541
@dataclass
548542
class GPSMain(AirOSDataClass):

airos/discovery.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def parse_airos_packet(self, data: bytes, host_ip: str) -> dict[str, Any] | None
151151
log = f"Truncated MAC address TLV (Type 0x06). Expected {expected_length}, got {len(data) - offset} bytes. Remaining: {data[offset:].hex()}"
152152
_LOGGER.warning(log)
153153
log = f"Malformed packet: {log}"
154-
raise AirOSEndpointError(log)
154+
raise AirOSEndpointError(log) from None # noqa: TRY301
155155

156156
elif tlv_type in [
157157
0x02,
@@ -169,7 +169,7 @@ def parse_airos_packet(self, data: bytes, host_ip: str) -> dict[str, Any] | None
169169
log = f"Truncated TLV (Type {tlv_type:#x}), no 2-byte length field. Remaining: {data[offset:].hex()}"
170170
_LOGGER.warning(log)
171171
log = f"Malformed packet: {log}"
172-
raise AirOSEndpointError(log)
172+
raise AirOSEndpointError(log) from None # noqa: TRY301
173173

174174
tlv_length: int = struct.unpack_from(">H", data, offset)[0]
175175
offset += 2
@@ -181,7 +181,7 @@ def parse_airos_packet(self, data: bytes, host_ip: str) -> dict[str, Any] | None
181181
f"Data from TLV start: {data[offset - 3 :].hex()}"
182182
)
183183
_LOGGER.warning(log)
184-
raise AirOSEndpointError(f"Malformed packet: {log}")
184+
raise AirOSEndpointError(f"Malformed packet: {log}") from None # noqa: TRY301
185185

186186
tlv_value: bytes = data[offset : offset + tlv_length]
187187

@@ -194,7 +194,9 @@ def parse_airos_packet(self, data: bytes, host_ip: str) -> dict[str, Any] | None
194194
else:
195195
log = f"Unexpected length for 0x02 TLV (MAC+IP). Expected 10, got {tlv_length}. Value: {tlv_value.hex()}"
196196
_LOGGER.warning(log)
197-
raise AirOSEndpointError(f"Malformed packet: {log}")
197+
raise AirOSEndpointError( # noqa: TRY301
198+
f"Malformed packet: {log}"
199+
) from None
198200

199201
elif tlv_type == 0x03:
200202
parsed_info["firmware_version"] = tlv_value.decode(
@@ -213,7 +215,9 @@ def parse_airos_packet(self, data: bytes, host_ip: str) -> dict[str, Any] | None
213215
else:
214216
log = f"Unexpected length for Uptime (Type 0x0A): {tlv_length}. Value: {tlv_value.hex()}"
215217
_LOGGER.warning(log)
216-
raise AirOSEndpointError(f"Malformed packet: {log}")
218+
raise AirOSEndpointError( # noqa: TRY301
219+
f"Malformed packet: {log}"
220+
) from None
217221

218222
elif tlv_type == 0x0B:
219223
parsed_info["hostname"] = tlv_value.decode(
@@ -260,7 +264,7 @@ def parse_airos_packet(self, data: bytes, host_ip: str) -> dict[str, Any] | None
260264
log += f"Cannot determine length, stopping parsing. Remaining: {data[offset - 1 :].hex()}"
261265
_LOGGER.warning(log)
262266
log = f"Malformed packet: {log}"
263-
raise AirOSEndpointError(log)
267+
raise AirOSEndpointError(log) from None # noqa: TRY301
264268

265269
except (struct.error, IndexError) as err:
266270
log = f"Parsing error (struct/index) in AirOSDiscoveryProtocol: {err} at offset {offset}. Remaining data: {data[offset:].hex()}"

mypy.ini

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
[mypy]
2+
python_version = 3.13
3+
platform = linux
4+
plugins = pydantic.mypy, pydantic.v1.mypy
5+
show_error_codes = true
6+
follow_imports = normal
7+
local_partial_types = true
8+
strict_equality = true
9+
strict_bytes = true
10+
no_implicit_optional = true
11+
warn_incomplete_stub = true
12+
warn_redundant_casts = true
13+
warn_unused_configs = true
14+
warn_unused_ignores = true
15+
enable_error_code = deprecated, ignore-without-code, redundant-self, truthy-iterable
16+
disable_error_code = annotation-unchecked, import-not-found, import-untyped
17+
extra_checks = false
18+
check_untyped_defs = true
19+
disallow_incomplete_defs = true
20+
disallow_subclassing_any = true
21+
disallow_untyped_calls = true
22+
disallow_untyped_decorators = true
23+
disallow_untyped_defs = true
24+
warn_return_any = true
25+
warn_unreachable = true
26+
27+
[pydantic-mypy]
28+
init_forbid_extra = true
29+
init_typed = true
30+
warn_required_dynamic_aliases = true
31+
warn_untyped_fields = true
32+
33+
[mypy-airos.*]
34+
no_implicit_reexport = true
35+
36+
[mypy-tests.*]
37+
check_untyped_defs = false
38+
disallow_incomplete_defs = false
39+
disallow_subclassing_any = false
40+
disallow_untyped_calls = false
41+
disallow_untyped_decorators = false
42+
disallow_untyped_defs = false
43+
warn_return_any = false
44+
warn_unreachable = false

0 commit comments

Comments
 (0)