mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-07 17:26:32 +00:00
Update to python 3.11 (#4296)
This commit is contained in:
parent
61a7e6a87d
commit
5ced4e2f3b
6
.github/workflows/builder.yml
vendored
6
.github/workflows/builder.yml
vendored
@ -33,12 +33,12 @@ on:
|
||||
- setup.py
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.10"
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
BUILD_NAME: supervisor
|
||||
BUILD_TYPE: supervisor
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.ref }}'
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
@ -104,7 +104,7 @@ jobs:
|
||||
if: needs.init.outputs.requirements == 'true'
|
||||
uses: home-assistant/wheels@2023.04.0
|
||||
with:
|
||||
abi: cp310
|
||||
abi: cp311
|
||||
tag: musllinux_1_2
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -8,7 +8,7 @@ on:
|
||||
pull_request: ~
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.10"
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
PRE_COMMIT_HOME: ~/.cache/pre-commit
|
||||
DEFAULT_CAS: v1.0.2
|
||||
|
||||
|
10
build.yaml
10
build.yaml
@ -1,11 +1,11 @@
|
||||
image: homeassistant/{arch}-hassio-supervisor
|
||||
shadow_repository: ghcr.io/home-assistant
|
||||
build_from:
|
||||
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.10-alpine3.16
|
||||
armhf: ghcr.io/home-assistant/armhf-base-python:3.10-alpine3.16
|
||||
armv7: ghcr.io/home-assistant/armv7-base-python:3.10-alpine3.16
|
||||
amd64: ghcr.io/home-assistant/amd64-base-python:3.10-alpine3.16
|
||||
i386: ghcr.io/home-assistant/i386-base-python:3.10-alpine3.16
|
||||
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.11-alpine3.16
|
||||
armhf: ghcr.io/home-assistant/armhf-base-python:3.11-alpine3.16
|
||||
armv7: ghcr.io/home-assistant/armv7-base-python:3.11-alpine3.16
|
||||
amd64: ghcr.io/home-assistant/amd64-base-python:3.11-alpine3.16
|
||||
i386: ghcr.io/home-assistant/i386-base-python:3.11-alpine3.16
|
||||
codenotary:
|
||||
signer: notary@home-assistant.io
|
||||
base_image: notary@home-assistant.io
|
||||
|
@ -5,7 +5,6 @@ atomicwrites-homeassistant==1.4.1
|
||||
attrs==23.1.0
|
||||
awesomeversion==23.5.0
|
||||
brotli==1.0.9
|
||||
cchardet==2.1.7
|
||||
ciso8601==2.3.0
|
||||
colorlog==6.7.0
|
||||
cpe==1.2.1
|
||||
@ -14,6 +13,7 @@ debugpy==1.6.7
|
||||
deepmerge==1.1.0
|
||||
dirhash==0.2.1
|
||||
docker==6.1.2
|
||||
faust-cchardet==2.1.18
|
||||
gitpython==3.1.31
|
||||
jinja2==3.1.2
|
||||
pulsectl==23.5.1
|
||||
|
@ -79,7 +79,7 @@ class AddonManager(CoreSysAttributes):
|
||||
tasks = []
|
||||
for slug in self.data.system:
|
||||
addon = self.local[slug] = Addon(self.coresys, slug)
|
||||
tasks.append(addon.load())
|
||||
tasks.append(self.sys_create_task(addon.load()))
|
||||
|
||||
# Run initial tasks
|
||||
_LOGGER.info("Found %d installed add-ons", len(tasks))
|
||||
|
@ -148,8 +148,8 @@ class APIIngress(CoreSysAttributes):
|
||||
# Proxy requests
|
||||
await asyncio.wait(
|
||||
[
|
||||
_websocket_forward(ws_server, ws_client),
|
||||
_websocket_forward(ws_client, ws_server),
|
||||
self.sys_create_task(_websocket_forward(ws_server, ws_client)),
|
||||
self.sys_create_task(_websocket_forward(ws_client, ws_server)),
|
||||
],
|
||||
return_when=asyncio.FIRST_COMPLETED,
|
||||
)
|
||||
|
@ -117,7 +117,7 @@ class BackupManager(FileConfiguration, CoreSysAttributes):
|
||||
self._backups[backup.slug] = backup
|
||||
|
||||
tasks = [
|
||||
_load_backup(tar_file)
|
||||
self.sys_create_task(_load_backup(tar_file))
|
||||
for path in self.backup_locations
|
||||
for tar_file in path.glob("*.tar")
|
||||
]
|
||||
|
@ -285,9 +285,12 @@ class Core(CoreSysAttributes):
|
||||
async with async_timeout.timeout(10):
|
||||
await asyncio.wait(
|
||||
[
|
||||
self.sys_api.stop(),
|
||||
self.sys_scheduler.shutdown(),
|
||||
self.sys_docker.unload(),
|
||||
self.sys_create_task(coro)
|
||||
for coro in (
|
||||
self.sys_api.stop(),
|
||||
self.sys_scheduler.shutdown(),
|
||||
self.sys_docker.unload(),
|
||||
)
|
||||
]
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
@ -298,10 +301,13 @@ class Core(CoreSysAttributes):
|
||||
async with async_timeout.timeout(10):
|
||||
await asyncio.wait(
|
||||
[
|
||||
self.sys_websession.close(),
|
||||
self.sys_ingress.unload(),
|
||||
self.sys_hardware.unload(),
|
||||
self.sys_dbus.unload(),
|
||||
self.sys_create_task(coro)
|
||||
for coro in (
|
||||
self.sys_websession.close(),
|
||||
self.sys_ingress.unload(),
|
||||
self.sys_hardware.unload(),
|
||||
self.sys_dbus.unload(),
|
||||
)
|
||||
]
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
|
@ -258,7 +258,12 @@ class HomeAssistant(FileConfiguration, CoreSysAttributes):
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Prepare Home Assistant object."""
|
||||
await asyncio.wait([self.secrets.load(), self.core.load()])
|
||||
await asyncio.wait(
|
||||
[
|
||||
self.sys_create_task(self.secrets.load()),
|
||||
self.sys_create_task(self.core.load()),
|
||||
]
|
||||
)
|
||||
|
||||
# Register for events
|
||||
self.sys_bus.register_event(BusEvent.HARDWARE_NEW_DEVICE, self._hardware_events)
|
||||
|
@ -109,11 +109,18 @@ class MountManager(FileConfiguration, CoreSysAttributes):
|
||||
|
||||
# Bind all media mounts to directories in media
|
||||
if self.media_mounts:
|
||||
await asyncio.wait([self._bind_media(mount) for mount in self.media_mounts])
|
||||
await asyncio.wait(
|
||||
[
|
||||
self.sys_create_task(self._bind_media(mount))
|
||||
for mount in self.media_mounts
|
||||
]
|
||||
)
|
||||
|
||||
async def reload(self) -> None:
|
||||
"""Update mounts info via dbus and reload failed mounts."""
|
||||
await asyncio.wait([mount.update() for mount in self.mounts])
|
||||
await asyncio.wait(
|
||||
[self.sys_create_task(mount.update()) for mount in self.mounts]
|
||||
)
|
||||
|
||||
# Try to reload any newly failed mounts and report issues if failure persists
|
||||
new_failures = [
|
||||
|
@ -107,7 +107,9 @@ class PluginManager(CoreSysAttributes):
|
||||
|
||||
async def repair(self) -> None:
|
||||
"""Repair Supervisor plugins."""
|
||||
await asyncio.wait([plugin.repair() for plugin in self.all_plugins])
|
||||
await asyncio.wait(
|
||||
[self.sys_create_task(plugin.repair()) for plugin in self.all_plugins]
|
||||
)
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""Shutdown Supervisor plugin."""
|
||||
|
@ -83,7 +83,7 @@ class StoreManager(CoreSysAttributes, FileConfiguration):
|
||||
@Job(conditions=[JobCondition.SUPERVISOR_UPDATED], on_condition=StoreJobError)
|
||||
async def reload(self) -> None:
|
||||
"""Update add-ons from repository and reload list."""
|
||||
tasks = [repository.update() for repository in self.all]
|
||||
tasks = [self.sys_create_task(repository.update()) for repository in self.all]
|
||||
if tasks:
|
||||
await asyncio.wait(tasks)
|
||||
|
||||
|
@ -191,7 +191,7 @@ class Updater(FileConfiguration, CoreSysAttributes):
|
||||
|
||||
Is a coroutine.
|
||||
"""
|
||||
url = URL_HASSIO_VERSION.format(channel=self.channel)
|
||||
url = URL_HASSIO_VERSION.format(channel=self.channel.value)
|
||||
machine = self.sys_machine or "default"
|
||||
|
||||
# Get data
|
||||
|
@ -69,14 +69,14 @@ async def cas_validate(
|
||||
|
||||
async with async_timeout.timeout(15):
|
||||
data, error = await proc.communicate()
|
||||
except OSError as err:
|
||||
raise CodeNotaryError(
|
||||
f"CodeNotary fatal error: {err!s}", _LOGGER.critical
|
||||
) from err
|
||||
except asyncio.TimeoutError:
|
||||
raise CodeNotaryBackendError(
|
||||
"Timeout while processing CodeNotary", _LOGGER.warning
|
||||
) from None
|
||||
except OSError as err:
|
||||
raise CodeNotaryError(
|
||||
f"CodeNotary fatal error: {err!s}", _LOGGER.critical
|
||||
) from err
|
||||
|
||||
# Check if Notarized
|
||||
if proc.returncode != 0 and not data:
|
||||
|
@ -18,6 +18,7 @@ from supervisor.exceptions import (
|
||||
DockerAPIError,
|
||||
DockerNotFound,
|
||||
)
|
||||
from supervisor.plugins.dns import PluginDns
|
||||
from supervisor.utils import check_exception_chain
|
||||
|
||||
from tests.common import load_json_fixture
|
||||
@ -164,3 +165,20 @@ async def test_addon_uninstall_removes_discovery(
|
||||
|
||||
assert coresys.addons.installed == []
|
||||
assert coresys.discovery.list_messages == []
|
||||
|
||||
|
||||
async def test_load(
|
||||
coresys: CoreSys, install_addon_ssh: Addon, caplog: pytest.LogCaptureFixture
|
||||
):
|
||||
"""Test addon manager load."""
|
||||
caplog.clear()
|
||||
|
||||
with patch.object(DockerInterface, "attach") as attach, patch.object(
|
||||
PluginDns, "write_hosts"
|
||||
) as write_hosts:
|
||||
await coresys.addons.load()
|
||||
|
||||
attach.assert_called_once_with(version=AwesomeVersion("9.2.1"))
|
||||
write_hosts.assert_called_once()
|
||||
|
||||
assert "Found 1 installed add-ons" in caplog.text
|
||||
|
20
tests/homeassistant/test_module.py
Normal file
20
tests/homeassistant/test_module.py
Normal file
@ -0,0 +1,20 @@
|
||||
"""Test Homeassistant module."""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.docker.interface import DockerInterface
|
||||
|
||||
|
||||
async def test_load(coresys: CoreSys, tmp_supervisor_data: Path):
|
||||
"""Test homeassistant module load."""
|
||||
with open(tmp_supervisor_data / "homeassistant" / "secrets.yaml", "w") as secrets:
|
||||
secrets.write("hello: world\n")
|
||||
|
||||
with patch.object(DockerInterface, "attach") as attach:
|
||||
await coresys.homeassistant.load()
|
||||
|
||||
attach.assert_called_once()
|
||||
|
||||
assert coresys.homeassistant.secrets.secrets == {"hello": "world"}
|
31
tests/plugins/test_plugin_manager.py
Normal file
31
tests/plugins/test_plugin_manager.py
Normal file
@ -0,0 +1,31 @@
|
||||
"""Test plugin manager."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.docker.interface import DockerInterface
|
||||
|
||||
|
||||
def mock_awaitable_bool(value: bool):
|
||||
"""Return a mock of an awaitable bool."""
|
||||
|
||||
async def _mock_bool(*args, **kwargs) -> bool:
|
||||
return value
|
||||
|
||||
return _mock_bool
|
||||
|
||||
|
||||
async def test_repair(coresys: CoreSys):
|
||||
"""Test repair."""
|
||||
with patch.object(DockerInterface, "install") as install:
|
||||
# If instance exists, repair does nothing
|
||||
with patch.object(DockerInterface, "exists", new=mock_awaitable_bool(True)):
|
||||
await coresys.plugins.repair()
|
||||
|
||||
install.assert_not_called()
|
||||
|
||||
# If not, repair installs the image
|
||||
with patch.object(DockerInterface, "exists", new=mock_awaitable_bool(False)):
|
||||
await coresys.plugins.repair()
|
||||
|
||||
assert install.call_count == len(coresys.plugins.all_plugins)
|
@ -15,6 +15,7 @@ from supervisor.exceptions import AddonsNotSupportedError, StoreJobError
|
||||
from supervisor.homeassistant.module import HomeAssistant
|
||||
from supervisor.store import StoreManager
|
||||
from supervisor.store.addon import AddonStore
|
||||
from supervisor.store.git import GitRepo
|
||||
from supervisor.store.repository import Repository
|
||||
|
||||
from tests.common import load_yaml_fixture
|
||||
@ -231,3 +232,14 @@ async def test_install_unavailable_addon(
|
||||
await coresys.addons.install("local_ssh")
|
||||
|
||||
assert log in caplog.text
|
||||
|
||||
|
||||
async def test_reload(coresys: CoreSys):
|
||||
"""Test store reload."""
|
||||
await coresys.store.load()
|
||||
assert len(coresys.store.all) == 4
|
||||
|
||||
with patch.object(GitRepo, "pull") as git_pull:
|
||||
await coresys.store.reload()
|
||||
|
||||
assert git_pull.call_count == 3
|
||||
|
Loading…
x
Reference in New Issue
Block a user