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:
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/builder.yml
vendored
4
.github/workflows/builder.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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/.*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user