Fix three code-smell comments in updater and firmware port #105

Merged
Claude merged 3 commits from chore/updater-status-and-port into refactor/code-hygiene 2026-09-28 07:48:45 +00:00
Collaborator

Three related cleanups, each removing a comment that existed because the code was awkward:

  • UpdateStatus.version is now str | None, dropping the empty-string sentinel and the or ""/or None conversions in updater/link.py and updater/api.py.
  • FirmwareCache.images() sorts once instead of twice, with a combined (environment, version-descending) key.
  • CONTROL_API_PORT is now defined once in custom_components/campervan/protocol/__init__.py and imported by both updater/config.py and the integration's firmware.py, replacing the duplicated UPDATER_PORT = 8787 and the test that kept the two literals in step.

Behaviour unchanged; tools/check is green (1222 passed).

Three related cleanups, each removing a comment that existed because the code was awkward: - `UpdateStatus.version` is now `str | None`, dropping the empty-string sentinel and the `or ""`/`or None` conversions in `updater/link.py` and `updater/api.py`. - `FirmwareCache.images()` sorts once instead of twice, with a combined `(environment, version-descending)` key. - `CONTROL_API_PORT` is now defined once in `custom_components/campervan/protocol/__init__.py` and imported by both `updater/config.py` and the integration's `firmware.py`, replacing the duplicated `UPDATER_PORT = 8787` and the test that kept the two literals in step. Behaviour unchanged; `tools/check` is green (1222 passed).
Define the control API port once, in protocol
All checks were successful
Lint, type check and test / hassfest (pull_request) Successful in 9s
Lint, type check and test / quality (pull_request) Successful in 1m49s
Lint, type check and test / release (pull_request) Has been skipped
340ecfbe28
Claude merged commit e98999bf72 into refactor/code-hygiene 2026-09-28 07:48:45 +00:00
Claude deleted branch chore/updater-status-and-port 2026-09-28 07:48:45 +00:00
Author
Collaborator

Review (can't leave a formal review on my own PR, so posting as a comment):

updater/firmware.py, the images() sort (blocking) — the one-pass sort isn't behaviourally equivalent to the old two-pass sort.

It differs when two images in the same environment have version strings with a different number of dot-separated segments (one a proper prefix of the other) — version_order doesn't require fixed arity, so nothing rules this out.

Elementwise negation correctly reverses comparison between equal-length tuples, but Python's tuple comparison also has a "shorter is less" rule when one tuple is a prefix of another, and negation doesn't flip that rule (a prefix stays a prefix, whatever the sign of its elements). Concrete example, same environment:

  • "2.1" → version_order = (2, 1)
  • "2.1.5" → version_order = (2, 1, 5)

Old code (reverse=True over version_order): (2,1) < (2,1,5), so reversed puts "2.1.5" first — newest first, correct.

New code (negate, then ascending): (-2,-1) < (-2,-1,-5) — still a prefix relation, unaffected by negation — so ascending puts "2.1" first. The shorter version now sorts as "newest", which is backwards.

In practice every version that reaches the cache today is a 3-part CalVer string, so this is probably dormant — but the ASSET regex's version group ([^-]+) doesn't itself enforce that arity, so nothing guarantees it. Either add a comment naming the fixed-arity assumption this key now relies on, or fix the key so it doesn't depend on negation reversing prefix order, and add a test with two same-environment versions of different segment counts.

Minor, non-blocking: CONTROL_API_PORT now lives in custom_components/campervan/protocol/__init__.py, whose module docstring says "The wire format, in Python." A control-API port is a deployment detail, not part of the wire format — a small conceptual mismatch with where it's homed, even though the one-way-dependency reasoning for putting it there is sound.

Everything else checks out: the UpdateStatus.version sentinel removal is complete and consistent (no leftover ""-assuming code paths), and the firmware.py diff only touches the UPDATER_HOST/UPDATER_PORT block plus its import line, respecting the concurrent-edit constraint.

Review (can't leave a formal review on my own PR, so posting as a comment): **`updater/firmware.py`, the `images()` sort (blocking) — the one-pass sort isn't behaviourally equivalent to the old two-pass sort.** It differs when two images in the same environment have version strings with a different number of dot-separated segments (one a proper prefix of the other) — `version_order` doesn't require fixed arity, so nothing rules this out. Elementwise negation correctly reverses comparison between equal-length tuples, but Python's tuple comparison also has a "shorter is less" rule when one tuple is a prefix of another, and negation doesn't flip that rule (a prefix stays a prefix, whatever the sign of its elements). Concrete example, same environment: - `"2.1"` → `version_order` = `(2, 1)` - `"2.1.5"` → `version_order` = `(2, 1, 5)` Old code (`reverse=True` over `version_order`): `(2,1) < (2,1,5)`, so reversed puts `"2.1.5"` first — newest first, correct. New code (negate, then ascending): `(-2,-1) < (-2,-1,-5)` — still a prefix relation, unaffected by negation — so ascending puts `"2.1"` first. The shorter version now sorts as "newest", which is backwards. In practice every version that reaches the cache today is a 3-part CalVer string, so this is probably dormant — but the `ASSET` regex's version group (`[^-]+`) doesn't itself enforce that arity, so nothing guarantees it. Either add a comment naming the fixed-arity assumption this key now relies on, or fix the key so it doesn't depend on negation reversing prefix order, and add a test with two same-environment versions of different segment counts. **Minor, non-blocking:** `CONTROL_API_PORT` now lives in `custom_components/campervan/protocol/__init__.py`, whose module docstring says "The wire format, in Python." A control-API port is a deployment detail, not part of the wire format — a small conceptual mismatch with where it's homed, even though the one-way-dependency reasoning for putting it there is sound. Everything else checks out: the `UpdateStatus.version` sentinel removal is complete and consistent (no leftover `""`-assuming code paths), and the `firmware.py` diff only touches the `UPDATER_HOST`/`UPDATER_PORT` block plus its import line, respecting the concurrent-edit constraint.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/CampervanHomeAssistant!105
No description provided.