1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2026-04-02 00:07:16 +01:00

Use Python 3.14(.3) in CI and base image (#6586)

* Use Python 3.14(.3) in CI and base image

Update base image to the latest tag using Python 3.14.3 and update Python
version in CI workflows to 3.14.

With Python 3.14, backports.zstd is no longer necessary as it's now available
in the standard library.

* Update wheels ABI in the wheels builder to cp314

* Use explicit Python fix version in GH actions

Specify explicitly Python 3.14.3, as the setup-python action otherwise default
to 3.14.2 when 3.14.3, leading to different version in CI and in production.

* Update Python version references in pyproject.toml

* Fix all ruff quoted-annotation (UP037) errors

* Revert unquoting of DBus types in tests and ignore UP037 where needed
This commit is contained in:
Jan Čermák
2026-03-05 21:11:25 +01:00
committed by GitHub
parent 80f790bf5d
commit b1be897439
17 changed files with 29 additions and 30 deletions

View File

@@ -91,8 +91,8 @@ availability.
### Python Requirements
- **Compatibility**: Python 3.13+
- **Language Features**: Use modern Python features:
- **Compatibility**: Python 3.14+
- **Language Features**: Use modern Python features:
- Type hints with `typing` module
- f-strings (preferred over `%` or `.format()`)
- Dataclasses and enum classes

View File

@@ -33,7 +33,7 @@ on:
- setup.py
env:
DEFAULT_PYTHON: "3.13"
DEFAULT_PYTHON: "3.14.3"
COSIGN_VERSION: "v2.5.3"
CRANE_VERSION: "v0.20.7"
CRANE_SHA256: "8ef3564d264e6b5ca93f7b7f5652704c4dd29d33935aff6947dd5adefd05953e"
@@ -106,7 +106,7 @@ jobs:
- runs-on: ubuntu-24.04-arm
arch: aarch64
env:
WHEELS_ABI: cp313
WHEELS_ABI: cp314
WHEELS_TAG: musllinux_1_2
WHEELS_APK_DEPS: "libffi-dev;openssl-dev;yaml-dev"
WHEELS_SKIP_BINARY: aiohttp

View File

@@ -8,7 +8,7 @@ on:
pull_request: ~
env:
DEFAULT_PYTHON: "3.13"
DEFAULT_PYTHON: "3.14.3"
PRE_COMMIT_CACHE: ~/.cache/pre-commit
MYPY_CACHE_VERSION: 1

View File

@@ -1,7 +1,7 @@
image: ghcr.io/home-assistant/{arch}-hassio-supervisor
build_from:
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.13-alpine3.22-2025.12.2
amd64: ghcr.io/home-assistant/amd64-base-python:3.13-alpine3.22-2025.12.2
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.14-alpine3.22-2026.02.0
amd64: ghcr.io/home-assistant/amd64-base-python:3.14-alpine3.22-2026.02.0
cosign:
base_identity: https://github.com/home-assistant/docker-base/.*
identity: https://github.com/home-assistant/supervisor/.*

View File

@@ -12,7 +12,7 @@ authors = [
{ name = "The Home Assistant Authors", email = "hello@home-assistant.io" },
]
keywords = ["docker", "home-assistant", "api"]
requires-python = ">=3.13.0"
requires-python = ">=3.14.0"
[project.urls]
"Homepage" = "https://www.home-assistant.io/"
@@ -31,7 +31,7 @@ include-package-data = true
include = ["supervisor*"]
[tool.pylint.MAIN]
py-version = "3.13"
py-version = "3.14"
# Use a conservative default here; 2 should speed up most setups and not hurt
# any too bad. Override on command line as appropriate.
jobs = 2
@@ -368,7 +368,7 @@ split-on-trailing-comma = false
[tool.ruff.lint.per-file-ignores]
# DBus Service Mocks must use typing and names understood by dbus-fast
"tests/dbus_service_mocks/*.py" = ["F722", "F821", "N815"]
"tests/dbus_service_mocks/*.py" = ["F722", "F821", "N815", "UP037"]
[tool.ruff.lint.mccabe]
max-complexity = 25

View File

@@ -4,7 +4,6 @@ aiohttp==3.13.3
atomicwrites-homeassistant==1.4.1
attrs==25.4.0
awesomeversion==25.8.0
backports.zstd==1.3.0
blockbuster==1.5.26
brotli==1.2.0
ciso8601==2.3.3

View File

@@ -32,7 +32,7 @@ class DBusStrEnum(StrEnum):
"""StrEnum that tolerates unknown values from D-Bus."""
@classmethod
def _missing_(cls, value: object) -> "DBusStrEnum | None":
def _missing_(cls, value: object) -> DBusStrEnum | None:
if not isinstance(value, str):
return None
_report_unknown_value(cls, value)
@@ -46,7 +46,7 @@ class DBusIntEnum(IntEnum):
"""IntEnum that tolerates unknown values from D-Bus."""
@classmethod
def _missing_(cls, value: object) -> "DBusIntEnum | None":
def _missing_(cls, value: object) -> DBusIntEnum | None:
if not isinstance(value, int):
return None
_report_unknown_value(cls, value)

View File

@@ -72,7 +72,7 @@ class UDisks2Block(DBusInterfaceProxy):
@staticmethod
async def new(
object_path: str, bus: MessageBus, *, sync_properties: bool = True
) -> "UDisks2Block":
) -> UDisks2Block:
"""Create and connect object."""
obj = UDisks2Block(object_path, sync_properties=sync_properties)
await obj.connect(bus)

View File

@@ -46,7 +46,7 @@ class DeviceSpecification:
partlabel: str | None = None
@staticmethod
def from_dict(data: DeviceSpecificationDataType) -> "DeviceSpecification":
def from_dict(data: DeviceSpecificationDataType) -> DeviceSpecification:
"""Create DeviceSpecification from dict."""
return DeviceSpecification(
path=Path(data["path"]) if "path" in data else None,
@@ -108,7 +108,7 @@ class FormatOptions:
auth_no_user_interaction: bool | None = None
@staticmethod
def from_dict(data: FormatOptionsDataType) -> "FormatOptions":
def from_dict(data: FormatOptionsDataType) -> FormatOptions:
"""Create FormatOptions from dict."""
return FormatOptions(
label=data.get("label"),
@@ -182,7 +182,7 @@ class MountOptions:
auth_no_user_interaction: bool | None = None
@staticmethod
def from_dict(data: MountOptionsDataType) -> "MountOptions":
def from_dict(data: MountOptionsDataType) -> MountOptions:
"""Create MountOptions from dict."""
return MountOptions(
fstype=data.get("fstype"),
@@ -226,7 +226,7 @@ class UnmountOptions:
auth_no_user_interaction: bool | None = None
@staticmethod
def from_dict(data: UnmountOptionsDataType) -> "UnmountOptions":
def from_dict(data: UnmountOptionsDataType) -> UnmountOptions:
"""Create MountOptions from dict."""
return UnmountOptions(
force=data.get("force"),
@@ -268,7 +268,7 @@ class CreatePartitionOptions:
auth_no_user_interaction: bool | None = None
@staticmethod
def from_dict(data: CreatePartitionOptionsDataType) -> "CreatePartitionOptions":
def from_dict(data: CreatePartitionOptionsDataType) -> CreatePartitionOptions:
"""Create CreatePartitionOptions from dict."""
return CreatePartitionOptions(
partition_type=data.get("partition-type"),
@@ -310,7 +310,7 @@ class DeletePartitionOptions:
auth_no_user_interaction: bool | None = None
@staticmethod
def from_dict(data: DeletePartitionOptionsDataType) -> "DeletePartitionOptions":
def from_dict(data: DeletePartitionOptionsDataType) -> DeletePartitionOptions:
"""Create DeletePartitionOptions from dict."""
return DeletePartitionOptions(
tear_down=data.get("tear-down"),

View File

@@ -51,7 +51,7 @@ class UDisks2Drive(DBusInterfaceProxy):
await self._reload_interfaces()
@staticmethod
async def new(object_path: str, bus: MessageBus) -> "UDisks2Drive":
async def new(object_path: str, bus: MessageBus) -> UDisks2Drive:
"""Create and connect object."""
obj = UDisks2Drive(object_path)
await obj.connect(bus)

View File

@@ -96,7 +96,7 @@ class UDisks2NVMeController(DBusInterfaceProxy):
super().__init__()
@staticmethod
async def new(object_path: str, bus: MessageBus) -> "UDisks2NVMeController":
async def new(object_path: str, bus: MessageBus) -> UDisks2NVMeController:
"""Create and connect object."""
obj = UDisks2NVMeController(object_path)
await obj.connect(bus)

View File

@@ -153,7 +153,7 @@ class Interface:
)
@staticmethod
def from_dbus_interface(inet: NetworkInterface) -> "Interface":
def from_dbus_interface(inet: NetworkInterface) -> Interface:
"""Coerce a dbus interface into normal Interface."""
if inet.settings and inet.settings.ipv4:
ipv4_setting = IpSetting(

View File

@@ -59,7 +59,7 @@ class Mount(CoreSysAttributes, ABC):
)
@classmethod
def from_dict(cls, coresys: CoreSys, data: MountData) -> "Mount":
def from_dict(cls, coresys: CoreSys, data: MountData) -> Mount:
"""Make dictionary into mount object."""
if cls not in [Mount, NetworkMount]:
return cls(coresys, data)
@@ -562,7 +562,7 @@ class BindMount(Mount):
usage: MountUsage | None = None,
where: PurePath | None = None,
read_only: bool = False,
) -> "BindMount":
) -> BindMount:
"""Create a new bind mount instance."""
return BindMount(
coresys,

View File

@@ -57,7 +57,7 @@ class Disk:
@staticmethod
def from_udisks2_drive(
drive: UDisks2Drive, drive_block_device: UDisks2Block
) -> "Disk":
) -> Disk:
"""Convert UDisks2Drive into a Disk object."""
return Disk(
vendor=drive.vendor,

View File

@@ -52,7 +52,7 @@ class SlotStatus:
parent: str | None = None
@classmethod
def from_dict(cls, data: SlotStatusDataType) -> "SlotStatus":
def from_dict(cls, data: SlotStatusDataType) -> SlotStatus:
"""Create SlotStatus from dictionary."""
return cls(
class_=data["class"],

View File

@@ -28,12 +28,12 @@ class TestInterface(DBusServiceMock):
self.object_path = object_path
@signal(name="TestSignal")
def test_signal(self, value: str) -> "s": # noqa: F821
def test_signal(self, value: str) -> "s": # noqa: F821, UP037
"""Send test signal."""
return value
@dbus_property(access=PropertyAccess.READ, name="TestProp")
def test_prop(self) -> "u": # noqa: F821
def test_prop(self) -> "u": # noqa: F821, UP037
"""Get test property."""
return 4

View File

@@ -29,7 +29,7 @@ class TestInterface(DBusServiceMock):
object_path = DBUS_OBJECT_BASE
@method(name="Test")
def test(self, _: "b") -> None: # noqa: F821
def test(self, _: "b") -> None: # noqa: F821, UP037
"""Do Test method."""
@signal(name="Test")