Update Python to 3.13 (#5564)

* Bump Supervisor to Python 3.13

* Update ruff configuration to 0.9.1

Adjust pyproject.toml for ruff 0.9.1. Also make sure that latest version
of ruff is used in pre-commit.

* Set default configuration for pytest-asyncio

* Run ruff check

* Drop deprecated decorator no_type_check_decorator

The upstream PR (https://github.com/python/cpython/issues/106309) says
this never got really implemented by type checkers.

* Bump devcontainer to latest release
This commit is contained in:
Stefan Agner 2025-01-21 11:57:30 +01:00 committed by GitHub
parent fff3bfd01e
commit b7412b0679
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 31 additions and 33 deletions

View File

@ -1,6 +1,6 @@
{
"name": "Supervisor dev",
"image": "ghcr.io/home-assistant/devcontainer:supervisor",
"image": "ghcr.io/home-assistant/devcontainer:2-supervisor",
"containerEnv": {
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
},
@ -44,5 +44,8 @@
}
}
},
"mounts": ["type=volume,target=/var/lib/docker"]
"mounts": [
"type=volume,target=/var/lib/docker",
"type=volume,target=/mnt/supervisor"
]
}

View File

@ -33,7 +33,7 @@ on:
- setup.py
env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.13"
BUILD_NAME: supervisor
BUILD_TYPE: supervisor

View File

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

View File

@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.7
rev: v0.9.1
hooks:
- id: ruff
args:
@ -8,7 +8,7 @@ repos:
- id: ruff-format
files: ^((supervisor|tests)/.+)?[^/]+\.py$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v5.0.0
hooks:
- id: check-executables-have-shebangs
stages: [manual]

View File

@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-hassio-supervisor
build_from:
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.12-alpine3.20
armhf: ghcr.io/home-assistant/armhf-base-python:3.12-alpine3.20
armv7: ghcr.io/home-assistant/armv7-base-python:3.12-alpine3.20
amd64: ghcr.io/home-assistant/amd64-base-python:3.12-alpine3.20
i386: ghcr.io/home-assistant/i386-base-python:3.12-alpine3.20
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.13-alpine3.21
armhf: ghcr.io/home-assistant/armhf-base-python:3.13-alpine3.21
armv7: ghcr.io/home-assistant/armv7-base-python:3.13-alpine3.21
amd64: ghcr.io/home-assistant/amd64-base-python:3.13-alpine3.21
i386: ghcr.io/home-assistant/i386-base-python:3.13-alpine3.21
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io

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.12.0"
requires-python = ">=3.13.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.12"
py-version = "3.13"
# 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
@ -147,7 +147,7 @@ disable = [
# "pointless-statement", # B018, ruff catches new occurrences, needs more work
"raise-missing-from", # TRY200
# "redefined-builtin", # A001, ruff is way more stricter, needs work
"try-except-raise", # TRY302
"try-except-raise", # TRY203
"unused-argument", # ARG001, we don't use it
"unused-format-string-argument", #F507
"unused-format-string-key", # F504
@ -223,6 +223,7 @@ testpaths = ["tests"]
norecursedirs = [".git"]
log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s"
log_date_format = "%Y-%m-%d %H:%M:%S"
asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto"
filterwarnings = [
"error",
@ -289,7 +290,7 @@ lint.select = [
"T20", # flake8-print
"TID251", # Banned imports
"TRY004", # Prefer TypeError exception for invalid type
"TRY302", # Remove exception handler; error is immediately re-raised
"TRY203", # Remove exception handler; error is immediately re-raised
"UP", # pyupgrade
"W", # pycodestyle
]

View File

@ -3,7 +3,7 @@ coverage==7.6.10
pre-commit==4.1.0
pylint==3.3.3
pytest-aiohttp==1.0.5
pytest-asyncio==0.23.6
pytest-asyncio==0.25.2
pytest-cov==6.0.0
pytest-timeout==2.3.1
pytest==8.3.4

View File

@ -239,12 +239,12 @@ class APIHost(CoreSysAttributes):
# return 2 lines at minimum.
lines = max(2, lines)
# entries=cursor[[:num_skip]:num_entries]
range_header = f"entries=:-{lines-1}:{'' if follow else lines}"
range_header = f"entries=:-{lines - 1}:{'' if follow else lines}"
elif RANGE in request.headers:
range_header = request.headers.get(RANGE)
else:
range_header = (
f"entries=:-{DEFAULT_LINES-1}:{'' if follow else DEFAULT_LINES}"
f"entries=:-{DEFAULT_LINES - 1}:{'' if follow else DEFAULT_LINES}"
)
async with self.sys_host.logs.journald_logs(

View File

@ -61,7 +61,7 @@ def journal_verbose_formatter(entries: dict[str, str]) -> str:
async def journal_logs_reader(
journal_logs: ClientResponse, log_formatter: LogFormatter = LogFormatter.PLAIN
) -> AsyncGenerator[(str | None, str), None]:
) -> AsyncGenerator[str | None, str]:
"""Read logs from systemd journal line by line, formatted using the given formatter.
Returns a generator of (cursor, formatted_entry) tuples.

View File

@ -18,7 +18,7 @@ from supervisor.api.proxy import APIProxy
from supervisor.const import ATTR_ACCESS_TOKEN
def id_generator() -> Generator[int, None, None]:
def id_generator() -> Generator[int]:
"""Generate IDs for WS messages."""
i = 0
while True:

View File

@ -117,7 +117,7 @@ async def docker() -> DockerAPI:
@pytest.fixture(scope="session")
def dbus_session() -> Generator[str, None, None]:
def dbus_session() -> Generator[str]:
"""Start a dbus session.
Returns session address.

View File

@ -2,7 +2,7 @@
import asyncio
from functools import wraps
from typing import Any, no_type_check_decorator
from typing import Any
from dbus_fast import Message
from dbus_fast.aio.message_bus import MessageBus
@ -51,7 +51,6 @@ def dbus_method(name: str = None, disabled: bool = False, track_obj_path: bool =
"""
orig_decorator = method(name=name, disabled=disabled)
@no_type_check_decorator
def decorator(func):
calls: list[list[Any]] = []

View File

@ -50,9 +50,9 @@ def test_format_verbose_timestamp():
"MESSAGE": "x",
}
formatted = journal_verbose_formatter(fields)
assert formatted.startswith(
"1970-01-01 00:00:00.001 "
), f"Invalid log timestamp: {formatted}"
assert formatted.startswith("1970-01-01 00:00:00.001 "), (
f"Invalid log timestamp: {formatted}"
)
def test_format_verbose():
@ -143,10 +143,7 @@ async def test_parsing_two_messages():
"""Test reading multiple messages."""
journal_logs, stream = _journal_logs_mock()
stream.feed_data(
b"MESSAGE=Hello, world!\n"
b"ID=1\n\n"
b"MESSAGE=Hello again, world!\n"
b"ID=2\n\n"
b"MESSAGE=Hello, world!\nID=1\n\nMESSAGE=Hello again, world!\nID=2\n\n"
)
stream.feed_eof()
@ -184,9 +181,7 @@ async def test_parsing_malformed_binary_message():
"""Test that malformed binary message raises MalformedBinaryEntryError."""
journal_logs, stream = _journal_logs_mock()
stream.feed_data(
b"ID=1\n"
b"MESSAGE\n\x0d\x00\x00\x00\x00\x00\x00\x00Hello, world!"
b"AFTER=after\n\n"
b"ID=1\nMESSAGE\n\x0d\x00\x00\x00\x00\x00\x00\x00Hello, world!AFTER=after\n\n"
)
with pytest.raises(MalformedBinaryEntryError):