1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2026-02-14 23:19:37 +00:00

Improve CpuArch type safety with explicit conversions (#6524)

The CpuArch enum was being used inconsistently throughout the codebase,
with some code expecting enum values and other code expecting strings.
This caused type checking issues and potential runtime errors.

Changes:
- Fix match_base() to return CpuArch enum instead of str
- Add explicit string conversions using !s formatting where arch values
  are used in f-strings (build.py, model.py)
- Convert CpuArch to str explicitly in contexts requiring strings
  (docker/addon.py, misc/filter.py)
- Update all tests to use CpuArch enum values instead of strings
- Update test mocks to return CpuArch enum values

This ensures type consistency and improves MyPy type checking accuracy
across the architecture detection and management code.

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Stefan Agner
2026-02-04 11:34:23 +01:00
committed by GitHub
parent 2ee7e22bbd
commit a849050369
7 changed files with 35 additions and 31 deletions

View File

@@ -84,7 +84,7 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
def base_image(self) -> str:
"""Return base image for this add-on."""
if not self._data[ATTR_BUILD_FROM]:
return f"ghcr.io/home-assistant/{self.sys_arch.default}-base:latest"
return f"ghcr.io/home-assistant/{self.sys_arch.default!s}-base:latest"
if isinstance(self._data[ATTR_BUILD_FROM], str):
return self._data[ATTR_BUILD_FROM]

View File

@@ -725,4 +725,4 @@ class AddonModel(JobGroup, ABC):
return config[ATTR_IMAGE].format(arch=arch)
# local build
return f"{config[ATTR_REPOSITORY]}/{self.sys_arch.default}-addon-{config[ATTR_SLUG]}"
return f"{config[ATTR_REPOSITORY]}/{self.sys_arch.default!s}-addon-{config[ATTR_SLUG]}"

View File

@@ -85,7 +85,7 @@ class CpuArchManager(CoreSysAttributes):
"""Return best match for this CPU/Platform."""
for self_arch in self.supported:
if self_arch in arch_list:
return self_arch
return CpuArch(self_arch)
raise HassioArchNotFound()
def detect_cpu(self) -> CpuArch:

View File

@@ -129,7 +129,7 @@ class DockerAddon(DockerInterface):
def arch(self) -> str | None:
"""Return arch of Docker image."""
if self.addon.legacy:
return self.sys_arch.default
return str(self.sys_arch.default)
return super().arch
@property

View File

@@ -93,7 +93,7 @@ def filter_data(coresys: CoreSys, event: Event, hint: Hint) -> Event | None:
"installed_addons": installed_addons,
},
"host": {
"arch": coresys.arch.default,
"arch": str(coresys.arch.default),
"board": coresys.os.board,
"deployment": coresys.host.info.deployment,
"disk_free_space": coresys.hardware.disk.get_disk_free_space(

View File

@@ -14,7 +14,7 @@ import pytest
from supervisor.addons.addon import Addon
from supervisor.addons.build import AddonBuild
from supervisor.arch import CpuArchManager
from supervisor.const import AddonState
from supervisor.const import AddonState, CpuArch
from supervisor.coresys import CoreSys
from supervisor.docker.addon import DockerAddon
from supervisor.docker.const import ContainerState
@@ -666,10 +666,12 @@ async def test_addon_rebuild_fails_error(api_client: TestClient, coresys: CoreSy
with (
patch.object(
CpuArchManager, "supported", new=PropertyMock(return_value=["aarch64"])
CpuArchManager,
"supported",
new=PropertyMock(return_value=[CpuArch.AARCH64]),
),
patch.object(
CpuArchManager, "default", new=PropertyMock(return_value="aarch64")
CpuArchManager, "default", new=PropertyMock(return_value=CpuArch.AARCH64)
),
patch.object(
AddonBuild, "get_docker_args", return_value={"command": ["build"]}

View File

@@ -4,6 +4,8 @@ from unittest.mock import patch
import pytest
from supervisor.const import CpuArch
@pytest.fixture(name="mock_detect_cpu", autouse=True)
def mock_detect_cpu_fixture():
@@ -20,7 +22,7 @@ async def test_machine_not_exits(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "amd64"
assert coresys.arch.supported == ["amd64"]
assert coresys.arch.supported == [CpuArch.AMD64]
async def test_machine_not_exits_in_db(coresys, sys_machine, sys_supervisor):
@@ -30,7 +32,7 @@ async def test_machine_not_exits_in_db(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "amd64"
assert coresys.arch.supported == ["amd64"]
assert coresys.arch.supported == [CpuArch.AMD64]
async def test_supervisor_arch(coresys, sys_machine, sys_supervisor):
@@ -51,7 +53,7 @@ async def test_raspberrypi_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armhf"
assert coresys.arch.supported == ["armhf"]
assert coresys.arch.supported == [CpuArch.ARMHF]
async def test_raspberrypi2_arch(coresys, sys_machine, sys_supervisor):
@@ -61,7 +63,7 @@ async def test_raspberrypi2_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armv7"
assert coresys.arch.supported == ["armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF]
async def test_raspberrypi3_arch(coresys, sys_machine, sys_supervisor):
@@ -71,7 +73,7 @@ async def test_raspberrypi3_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armv7"
assert coresys.arch.supported == ["armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF]
async def test_raspberrypi3_64_arch(coresys, sys_machine, sys_supervisor):
@@ -81,7 +83,7 @@ async def test_raspberrypi3_64_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_raspberrypi4_arch(coresys, sys_machine, sys_supervisor):
@@ -91,7 +93,7 @@ async def test_raspberrypi4_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armv7"
assert coresys.arch.supported == ["armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF]
async def test_raspberrypi4_64_arch(coresys, sys_machine, sys_supervisor):
@@ -101,7 +103,7 @@ async def test_raspberrypi4_64_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_raspberrypi5_64_arch(coresys, sys_machine, sys_supervisor):
@@ -111,7 +113,7 @@ async def test_raspberrypi5_64_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_yellow_arch(coresys, sys_machine, sys_supervisor):
@@ -121,7 +123,7 @@ async def test_yellow_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
assert coresys.arch.is_supported(["aarch64"]) is True
assert coresys.arch.is_supported(["armv7"]) is True
assert coresys.arch.is_supported(["armhf"]) is True
@@ -135,7 +137,7 @@ async def test_green_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_tinker_arch(coresys, sys_machine, sys_supervisor):
@@ -145,7 +147,7 @@ async def test_tinker_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armv7"
assert coresys.arch.supported == ["armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF]
async def test_odroid_c2_arch(coresys, sys_machine, sys_supervisor):
@@ -155,7 +157,7 @@ async def test_odroid_c2_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_odroid_c4_arch(coresys, sys_machine, sys_supervisor):
@@ -165,7 +167,7 @@ async def test_odroid_c4_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_odroid_m1_arch(coresys, sys_machine, sys_supervisor):
@@ -175,7 +177,7 @@ async def test_odroid_m1_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_odroid_n2_arch(coresys, sys_machine, sys_supervisor):
@@ -185,7 +187,7 @@ async def test_odroid_n2_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64", "armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF]
async def test_odroid_xu_arch(coresys, sys_machine, sys_supervisor):
@@ -195,7 +197,7 @@ async def test_odroid_xu_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armv7"
assert coresys.arch.supported == ["armv7", "armhf"]
assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF]
async def test_intel_nuc_arch(coresys, sys_machine, sys_supervisor):
@@ -205,7 +207,7 @@ async def test_intel_nuc_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "amd64"
assert coresys.arch.supported == ["amd64", "i386"]
assert coresys.arch.supported == [CpuArch.AMD64, CpuArch.I386]
async def test_qemux86_arch(coresys, sys_machine, sys_supervisor):
@@ -215,7 +217,7 @@ async def test_qemux86_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "i386"
assert coresys.arch.supported == ["i386"]
assert coresys.arch.supported == [CpuArch.I386]
async def test_qemux86_64_arch(coresys, sys_machine, sys_supervisor):
@@ -225,7 +227,7 @@ async def test_qemux86_64_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "amd64"
assert coresys.arch.supported == ["amd64", "i386"]
assert coresys.arch.supported == [CpuArch.AMD64, CpuArch.I386]
async def test_qemuarm_arch(coresys, sys_machine, sys_supervisor):
@@ -235,7 +237,7 @@ async def test_qemuarm_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "armhf"
assert coresys.arch.supported == ["armhf"]
assert coresys.arch.supported == [CpuArch.ARMHF]
async def test_qemuarm_64_arch(coresys, sys_machine, sys_supervisor):
@@ -245,7 +247,7 @@ async def test_qemuarm_64_arch(coresys, sys_machine, sys_supervisor):
await coresys.arch.load()
assert coresys.arch.default == "aarch64"
assert coresys.arch.supported == ["aarch64"]
assert coresys.arch.supported == [CpuArch.AARCH64]
async def test_qemuarm_arch_native_armv7(
@@ -258,4 +260,4 @@ async def test_qemuarm_arch_native_armv7(
await coresys.arch.load()
assert coresys.arch.default == "armhf"
assert coresys.arch.supported == ["armhf", "armv7"]
assert coresys.arch.supported == [CpuArch.ARMHF, CpuArch.ARMV7]