mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-03 15:26:29 +00:00
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:
parent
fff3bfd01e
commit
b7412b0679
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Supervisor dev",
|
"name": "Supervisor dev",
|
||||||
"image": "ghcr.io/home-assistant/devcontainer:supervisor",
|
"image": "ghcr.io/home-assistant/devcontainer:2-supervisor",
|
||||||
"containerEnv": {
|
"containerEnv": {
|
||||||
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
|
"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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
2
.github/workflows/builder.yml
vendored
2
.github/workflows/builder.yml
vendored
@ -33,7 +33,7 @@ on:
|
|||||||
- setup.py
|
- setup.py
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_PYTHON: "3.12"
|
DEFAULT_PYTHON: "3.13"
|
||||||
BUILD_NAME: supervisor
|
BUILD_NAME: supervisor
|
||||||
BUILD_TYPE: supervisor
|
BUILD_TYPE: supervisor
|
||||||
|
|
||||||
|
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -8,7 +8,7 @@ on:
|
|||||||
pull_request: ~
|
pull_request: ~
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_PYTHON: "3.12"
|
DEFAULT_PYTHON: "3.13"
|
||||||
PRE_COMMIT_CACHE: ~/.cache/pre-commit
|
PRE_COMMIT_CACHE: ~/.cache/pre-commit
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.5.7
|
rev: v0.9.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args:
|
args:
|
||||||
@ -8,7 +8,7 @@ repos:
|
|||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
files: ^((supervisor|tests)/.+)?[^/]+\.py$
|
files: ^((supervisor|tests)/.+)?[^/]+\.py$
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.5.0
|
rev: v5.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-executables-have-shebangs
|
- id: check-executables-have-shebangs
|
||||||
stages: [manual]
|
stages: [manual]
|
||||||
|
10
build.yaml
10
build.yaml
@ -1,10 +1,10 @@
|
|||||||
image: ghcr.io/home-assistant/{arch}-hassio-supervisor
|
image: ghcr.io/home-assistant/{arch}-hassio-supervisor
|
||||||
build_from:
|
build_from:
|
||||||
aarch64: ghcr.io/home-assistant/aarch64-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.12-alpine3.20
|
armhf: ghcr.io/home-assistant/armhf-base-python:3.13-alpine3.21
|
||||||
armv7: ghcr.io/home-assistant/armv7-base-python:3.12-alpine3.20
|
armv7: ghcr.io/home-assistant/armv7-base-python:3.13-alpine3.21
|
||||||
amd64: ghcr.io/home-assistant/amd64-base-python:3.12-alpine3.20
|
amd64: ghcr.io/home-assistant/amd64-base-python:3.13-alpine3.21
|
||||||
i386: ghcr.io/home-assistant/i386-base-python:3.12-alpine3.20
|
i386: ghcr.io/home-assistant/i386-base-python:3.13-alpine3.21
|
||||||
codenotary:
|
codenotary:
|
||||||
signer: notary@home-assistant.io
|
signer: notary@home-assistant.io
|
||||||
base_image: notary@home-assistant.io
|
base_image: notary@home-assistant.io
|
||||||
|
@ -12,7 +12,7 @@ authors = [
|
|||||||
{ name = "The Home Assistant Authors", email = "hello@home-assistant.io" },
|
{ name = "The Home Assistant Authors", email = "hello@home-assistant.io" },
|
||||||
]
|
]
|
||||||
keywords = ["docker", "home-assistant", "api"]
|
keywords = ["docker", "home-assistant", "api"]
|
||||||
requires-python = ">=3.12.0"
|
requires-python = ">=3.13.0"
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
"Homepage" = "https://www.home-assistant.io/"
|
"Homepage" = "https://www.home-assistant.io/"
|
||||||
@ -31,7 +31,7 @@ include-package-data = true
|
|||||||
include = ["supervisor*"]
|
include = ["supervisor*"]
|
||||||
|
|
||||||
[tool.pylint.MAIN]
|
[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
|
# Use a conservative default here; 2 should speed up most setups and not hurt
|
||||||
# any too bad. Override on command line as appropriate.
|
# any too bad. Override on command line as appropriate.
|
||||||
jobs = 2
|
jobs = 2
|
||||||
@ -147,7 +147,7 @@ disable = [
|
|||||||
# "pointless-statement", # B018, ruff catches new occurrences, needs more work
|
# "pointless-statement", # B018, ruff catches new occurrences, needs more work
|
||||||
"raise-missing-from", # TRY200
|
"raise-missing-from", # TRY200
|
||||||
# "redefined-builtin", # A001, ruff is way more stricter, needs work
|
# "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-argument", # ARG001, we don't use it
|
||||||
"unused-format-string-argument", #F507
|
"unused-format-string-argument", #F507
|
||||||
"unused-format-string-key", # F504
|
"unused-format-string-key", # F504
|
||||||
@ -223,6 +223,7 @@ testpaths = ["tests"]
|
|||||||
norecursedirs = [".git"]
|
norecursedirs = [".git"]
|
||||||
log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s"
|
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"
|
log_date_format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
asyncio_default_fixture_loop_scope = "function"
|
||||||
asyncio_mode = "auto"
|
asyncio_mode = "auto"
|
||||||
filterwarnings = [
|
filterwarnings = [
|
||||||
"error",
|
"error",
|
||||||
@ -289,7 +290,7 @@ lint.select = [
|
|||||||
"T20", # flake8-print
|
"T20", # flake8-print
|
||||||
"TID251", # Banned imports
|
"TID251", # Banned imports
|
||||||
"TRY004", # Prefer TypeError exception for invalid type
|
"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
|
"UP", # pyupgrade
|
||||||
"W", # pycodestyle
|
"W", # pycodestyle
|
||||||
]
|
]
|
||||||
|
@ -3,7 +3,7 @@ coverage==7.6.10
|
|||||||
pre-commit==4.1.0
|
pre-commit==4.1.0
|
||||||
pylint==3.3.3
|
pylint==3.3.3
|
||||||
pytest-aiohttp==1.0.5
|
pytest-aiohttp==1.0.5
|
||||||
pytest-asyncio==0.23.6
|
pytest-asyncio==0.25.2
|
||||||
pytest-cov==6.0.0
|
pytest-cov==6.0.0
|
||||||
pytest-timeout==2.3.1
|
pytest-timeout==2.3.1
|
||||||
pytest==8.3.4
|
pytest==8.3.4
|
||||||
|
@ -239,12 +239,12 @@ class APIHost(CoreSysAttributes):
|
|||||||
# return 2 lines at minimum.
|
# return 2 lines at minimum.
|
||||||
lines = max(2, lines)
|
lines = max(2, lines)
|
||||||
# entries=cursor[[:num_skip]:num_entries]
|
# 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:
|
elif RANGE in request.headers:
|
||||||
range_header = request.headers.get(RANGE)
|
range_header = request.headers.get(RANGE)
|
||||||
else:
|
else:
|
||||||
range_header = (
|
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(
|
async with self.sys_host.logs.journald_logs(
|
||||||
|
@ -61,7 +61,7 @@ def journal_verbose_formatter(entries: dict[str, str]) -> str:
|
|||||||
|
|
||||||
async def journal_logs_reader(
|
async def journal_logs_reader(
|
||||||
journal_logs: ClientResponse, log_formatter: LogFormatter = LogFormatter.PLAIN
|
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.
|
"""Read logs from systemd journal line by line, formatted using the given formatter.
|
||||||
|
|
||||||
Returns a generator of (cursor, formatted_entry) tuples.
|
Returns a generator of (cursor, formatted_entry) tuples.
|
||||||
|
@ -18,7 +18,7 @@ from supervisor.api.proxy import APIProxy
|
|||||||
from supervisor.const import ATTR_ACCESS_TOKEN
|
from supervisor.const import ATTR_ACCESS_TOKEN
|
||||||
|
|
||||||
|
|
||||||
def id_generator() -> Generator[int, None, None]:
|
def id_generator() -> Generator[int]:
|
||||||
"""Generate IDs for WS messages."""
|
"""Generate IDs for WS messages."""
|
||||||
i = 0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
|
@ -117,7 +117,7 @@ async def docker() -> DockerAPI:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def dbus_session() -> Generator[str, None, None]:
|
def dbus_session() -> Generator[str]:
|
||||||
"""Start a dbus session.
|
"""Start a dbus session.
|
||||||
|
|
||||||
Returns session address.
|
Returns session address.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, no_type_check_decorator
|
from typing import Any
|
||||||
|
|
||||||
from dbus_fast import Message
|
from dbus_fast import Message
|
||||||
from dbus_fast.aio.message_bus import MessageBus
|
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)
|
orig_decorator = method(name=name, disabled=disabled)
|
||||||
|
|
||||||
@no_type_check_decorator
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
calls: list[list[Any]] = []
|
calls: list[list[Any]] = []
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@ def test_format_verbose_timestamp():
|
|||||||
"MESSAGE": "x",
|
"MESSAGE": "x",
|
||||||
}
|
}
|
||||||
formatted = journal_verbose_formatter(fields)
|
formatted = journal_verbose_formatter(fields)
|
||||||
assert formatted.startswith(
|
assert formatted.startswith("1970-01-01 00:00:00.001 "), (
|
||||||
"1970-01-01 00:00:00.001 "
|
f"Invalid log timestamp: {formatted}"
|
||||||
), f"Invalid log timestamp: {formatted}"
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_format_verbose():
|
def test_format_verbose():
|
||||||
@ -143,10 +143,7 @@ async def test_parsing_two_messages():
|
|||||||
"""Test reading multiple messages."""
|
"""Test reading multiple messages."""
|
||||||
journal_logs, stream = _journal_logs_mock()
|
journal_logs, stream = _journal_logs_mock()
|
||||||
stream.feed_data(
|
stream.feed_data(
|
||||||
b"MESSAGE=Hello, world!\n"
|
b"MESSAGE=Hello, world!\nID=1\n\nMESSAGE=Hello again, world!\nID=2\n\n"
|
||||||
b"ID=1\n\n"
|
|
||||||
b"MESSAGE=Hello again, world!\n"
|
|
||||||
b"ID=2\n\n"
|
|
||||||
)
|
)
|
||||||
stream.feed_eof()
|
stream.feed_eof()
|
||||||
|
|
||||||
@ -184,9 +181,7 @@ async def test_parsing_malformed_binary_message():
|
|||||||
"""Test that malformed binary message raises MalformedBinaryEntryError."""
|
"""Test that malformed binary message raises MalformedBinaryEntryError."""
|
||||||
journal_logs, stream = _journal_logs_mock()
|
journal_logs, stream = _journal_logs_mock()
|
||||||
stream.feed_data(
|
stream.feed_data(
|
||||||
b"ID=1\n"
|
b"ID=1\nMESSAGE\n\x0d\x00\x00\x00\x00\x00\x00\x00Hello, world!AFTER=after\n\n"
|
||||||
b"MESSAGE\n\x0d\x00\x00\x00\x00\x00\x00\x00Hello, world!"
|
|
||||||
b"AFTER=after\n\n"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(MalformedBinaryEntryError):
|
with pytest.raises(MalformedBinaryEntryError):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user