polish(sprint-0): clean code per ruff + mypy strict
Run the dev linters over Sprint 0's core/library/shared modules and address every finding. Behaviour unchanged; tests still 80/80 green. Changes: - Replace `class Foo(str, Enum)` with `class Foo(StrEnum)` (PEP 663 / Python 3.11+) in 7 enum classes: ActuatorType, AlarmSeverity, AlarmType, KnobMode, KnobFunction, AutopilotMode, AccessLevel, VesselType. Pydantic v2 serialises StrEnum the same way, so YAML/JSON round-trips are byte-identical. - Use `datetime.UTC` alias in place of `datetime.timezone.utc` (UP017) across alarms.py, knob_state.py, project_config.py, and test_knob_state.py. - Remove now-unnecessary forward-reference quotes from method return type annotations (UP037) — `from __future__ import annotations` is already in scope everywhere. - Tighten `_read_json_resource` / `_read_yaml_resource` in the library loader: validate that the deserialised payload is actually a dict before returning, instead of leaking `Any` from json.loads / yaml.safe_load. Fixes the only two `mypy --strict` findings. - Add `.claude/settings.local.json` to .gitignore (personal Claude Code overrides are not committed). Verification: ruff check arautopilot/ -> All checks passed mypy arautopilot/core library shared -> Success, 0 issues, 12 files pytest -> 80 passed in 0.25s Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,13 +11,13 @@ happens in the ESP32 firmware (Sprint 7).
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
from datetime import UTC, datetime
|
||||
from enum import StrEnum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
class KnobMode(str, Enum):
|
||||
class KnobMode(StrEnum):
|
||||
"""High-level state of the bridge knob."""
|
||||
|
||||
LIBRE = "libre"
|
||||
@@ -30,7 +30,7 @@ class KnobMode(str, Enum):
|
||||
"""Pending value being shown for operator confirmation."""
|
||||
|
||||
|
||||
class KnobFunction(str, Enum):
|
||||
class KnobFunction(StrEnum):
|
||||
"""The set of values the knob may target once armed."""
|
||||
|
||||
NONE = "none"
|
||||
@@ -84,7 +84,7 @@ class KnobState(BaseModel):
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _consistency(self) -> "KnobState":
|
||||
def _consistency(self) -> KnobState:
|
||||
if self.mode == KnobMode.LIBRE:
|
||||
if self.function != KnobFunction.NONE:
|
||||
raise ValueError("LIBRE mode requires function == NONE")
|
||||
@@ -105,7 +105,7 @@ class KnobState(BaseModel):
|
||||
|
||||
# --- Pure transition helpers (return new immutable states) --------------
|
||||
@classmethod
|
||||
def idle(cls) -> "KnobState":
|
||||
def idle(cls) -> KnobState:
|
||||
"""Construct the canonical idle (LIBRE) state."""
|
||||
return cls()
|
||||
|
||||
@@ -116,7 +116,7 @@ class KnobState(BaseModel):
|
||||
current_value: float,
|
||||
timeout_s: float = DEFAULT_ARMED_TIMEOUT_S,
|
||||
now: datetime | None = None,
|
||||
) -> "KnobState":
|
||||
) -> KnobState:
|
||||
"""Transition LIBRE → ARMADO for the given function."""
|
||||
if self.mode != KnobMode.LIBRE:
|
||||
raise ValueError(f"Cannot arm from mode {self.mode.value}")
|
||||
@@ -127,11 +127,11 @@ class KnobState(BaseModel):
|
||||
function=function,
|
||||
current_value=current_value,
|
||||
pending_value=None,
|
||||
armed_at=now or datetime.now(timezone.utc),
|
||||
armed_at=now or datetime.now(UTC),
|
||||
timeout_remaining_s=timeout_s,
|
||||
)
|
||||
|
||||
def propose(self, value: float, *, timeout_s: float = DEFAULT_ARMED_TIMEOUT_S) -> "KnobState":
|
||||
def propose(self, value: float, *, timeout_s: float = DEFAULT_ARMED_TIMEOUT_S) -> KnobState:
|
||||
"""Operator turned the knob: stage a pending value (ARMADO → CONFIRMANDO)."""
|
||||
if self.mode not in (KnobMode.ARMADO, KnobMode.CONFIRMANDO):
|
||||
raise ValueError(f"Cannot propose from mode {self.mode.value}")
|
||||
@@ -144,7 +144,7 @@ class KnobState(BaseModel):
|
||||
timeout_remaining_s=timeout_s,
|
||||
)
|
||||
|
||||
def confirm(self, *, timeout_s: float = DEFAULT_ARMED_TIMEOUT_S) -> "KnobState":
|
||||
def confirm(self, *, timeout_s: float = DEFAULT_ARMED_TIMEOUT_S) -> KnobState:
|
||||
"""Operator pressed to confirm; commits ``pending_value`` and stays armed."""
|
||||
if self.mode != KnobMode.CONFIRMANDO:
|
||||
raise ValueError(f"Cannot confirm from mode {self.mode.value}")
|
||||
@@ -157,6 +157,6 @@ class KnobState(BaseModel):
|
||||
timeout_remaining_s=timeout_s,
|
||||
)
|
||||
|
||||
def disarm(self) -> "KnobState":
|
||||
def disarm(self) -> KnobState:
|
||||
"""Force the knob back to LIBRE (timeout, alarm, mode change, long-press)."""
|
||||
return KnobState.idle()
|
||||
|
||||
Reference in New Issue
Block a user