mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-08 09:46:29 +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
|
- setup.py
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_PYTHON: "3.10"
|
DEFAULT_PYTHON: "3.11"
|
||||||
BUILD_NAME: supervisor
|
BUILD_NAME: supervisor
|
||||||
BUILD_TYPE: supervisor
|
BUILD_TYPE: supervisor
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: '${{ github.workflow }}-${{ github.ref }}'
|
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -104,7 +104,7 @@ jobs:
|
|||||||
if: needs.init.outputs.requirements == 'true'
|
if: needs.init.outputs.requirements == 'true'
|
||||||
uses: home-assistant/wheels@2023.04.0
|
uses: home-assistant/wheels@2023.04.0
|
||||||
with:
|
with:
|
||||||
abi: cp310
|
abi: cp311
|
||||||
tag: musllinux_1_2
|
tag: musllinux_1_2
|
||||||
arch: ${{ matrix.arch }}
|
arch: ${{ matrix.arch }}
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
|
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.10"
|
DEFAULT_PYTHON: "3.11"
|
||||||
PRE_COMMIT_HOME: ~/.cache/pre-commit
|
PRE_COMMIT_HOME: ~/.cache/pre-commit
|
||||||
DEFAULT_CAS: v1.0.2
|
DEFAULT_CAS: v1.0.2
|
||||||
|
|
||||||
|
10
build.yaml
10
build.yaml
@ -1,11 +1,11 @@
|
|||||||
image: homeassistant/{arch}-hassio-supervisor
|
image: homeassistant/{arch}-hassio-supervisor
|
||||||
shadow_repository: ghcr.io/home-assistant
|
shadow_repository: ghcr.io/home-assistant
|
||||||
build_from:
|
build_from:
|
||||||
aarch64: ghcr.io/home-assistant/aarch64-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.10-alpine3.16
|
armhf: ghcr.io/home-assistant/armhf-base-python:3.11-alpine3.16
|
||||||
armv7: ghcr.io/home-assistant/armv7-base-python:3.10-alpine3.16
|
armv7: ghcr.io/home-assistant/armv7-base-python:3.11-alpine3.16
|
||||||
amd64: ghcr.io/home-assistant/amd64-base-python:3.10-alpine3.16
|
amd64: ghcr.io/home-assistant/amd64-base-python:3.11-alpine3.16
|
||||||
i386: ghcr.io/home-assistant/i386-base-python:3.10-alpine3.16
|
i386: ghcr.io/home-assistant/i386-base-python:3.11-alpine3.16
|
||||||
codenotary:
|
codenotary:
|
||||||
signer: notary@home-assistant.io
|
signer: notary@home-assistant.io
|
||||||
base_image: notary@home-assistant.io
|
base_image: notary@home-assistant.io
|
||||||
|
@ -5,7 +5,6 @@ atomicwrites-homeassistant==1.4.1
|
|||||||
attrs==23.1.0
|
attrs==23.1.0
|
||||||
awesomeversion==23.5.0
|
awesomeversion==23.5.0
|
||||||
brotli==1.0.9
|
brotli==1.0.9
|
||||||
cchardet==2.1.7
|
|
||||||
ciso8601==2.3.0
|
ciso8601==2.3.0
|
||||||
colorlog==6.7.0
|
colorlog==6.7.0
|
||||||
cpe==1.2.1
|
cpe==1.2.1
|
||||||
@ -14,6 +13,7 @@ debugpy==1.6.7
|
|||||||
deepmerge==1.1.0
|
deepmerge==1.1.0
|
||||||
dirhash==0.2.1
|
dirhash==0.2.1
|
||||||
docker==6.1.2
|
docker==6.1.2
|
||||||
|
faust-cchardet==2.1.18
|
||||||
gitpython==3.1.31
|
gitpython==3.1.31
|
||||||
jinja2==3.1.2
|
jinja2==3.1.2
|
||||||
pulsectl==23.5.1
|
pulsectl==23.5.1
|
||||||
|
@ -79,7 +79,7 @@ class AddonManager(CoreSysAttributes):
|
|||||||
tasks = []
|
tasks = []
|
||||||
for slug in self.data.system:
|
for slug in self.data.system:
|
||||||
addon = self.local[slug] = Addon(self.coresys, slug)
|
addon = self.local[slug] = Addon(self.coresys, slug)
|
||||||
tasks.append(addon.load())
|
tasks.append(self.sys_create_task(addon.load()))
|
||||||
|
|
||||||
# Run initial tasks
|
# Run initial tasks
|
||||||
_LOGGER.info("Found %d installed add-ons", len(tasks))
|
_LOGGER.info("Found %d installed add-ons", len(tasks))
|
||||||
|
@ -148,8 +148,8 @@ class APIIngress(CoreSysAttributes):
|
|||||||
# Proxy requests
|
# Proxy requests
|
||||||
await asyncio.wait(
|
await asyncio.wait(
|
||||||
[
|
[
|
||||||
_websocket_forward(ws_server, ws_client),
|
self.sys_create_task(_websocket_forward(ws_server, ws_client)),
|
||||||
_websocket_forward(ws_client, ws_server),
|
self.sys_create_task(_websocket_forward(ws_client, ws_server)),
|
||||||
],
|
],
|
||||||
return_when=asyncio.FIRST_COMPLETED,
|
return_when=asyncio.FIRST_COMPLETED,
|
||||||
)
|
)
|
||||||
|
@ -117,7 +117,7 @@ class BackupManager(FileConfiguration, CoreSysAttributes):
|
|||||||
self._backups[backup.slug] = backup
|
self._backups[backup.slug] = backup
|
||||||
|
|
||||||
tasks = [
|
tasks = [
|
||||||
_load_backup(tar_file)
|
self.sys_create_task(_load_backup(tar_file))
|
||||||
for path in self.backup_locations
|
for path in self.backup_locations
|
||||||
for tar_file in path.glob("*.tar")
|
for tar_file in path.glob("*.tar")
|
||||||
]
|
]
|
||||||
|
@ -285,9 +285,12 @@ class Core(CoreSysAttributes):
|
|||||||
async with async_timeout.timeout(10):
|
async with async_timeout.timeout(10):
|
||||||
await asyncio.wait(
|
await asyncio.wait(
|
||||||
[
|
[
|
||||||
self.sys_api.stop(),
|
self.sys_create_task(coro)
|
||||||
self.sys_scheduler.shutdown(),
|
for coro in (
|
||||||
self.sys_docker.unload(),
|
self.sys_api.stop(),
|
||||||
|
self.sys_scheduler.shutdown(),
|
||||||
|
self.sys_docker.unload(),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
@ -298,10 +301,13 @@ class Core(CoreSysAttributes):
|
|||||||
async with async_timeout.timeout(10):
|
async with async_timeout.timeout(10):
|
||||||
await asyncio.wait(
|
await asyncio.wait(
|
||||||
[
|
[
|
||||||
self.sys_websession.close(),
|
self.sys_create_task(coro)
|
||||||
self.sys_ingress.unload(),
|
for coro in (
|
||||||
self.sys_hardware.unload(),
|
self.sys_websession.close(),
|
||||||
self.sys_dbus.unload(),
|
self.sys_ingress.unload(),
|
||||||
|
self.sys_hardware.unload(),
|
||||||
|
self.sys_dbus.unload(),
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
|
@ -258,7 +258,12 @@ class HomeAssistant(FileConfiguration, CoreSysAttributes):
|
|||||||
|
|
||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Prepare Home Assistant object."""
|
"""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
|
# Register for events
|
||||||
self.sys_bus.register_event(BusEvent.HARDWARE_NEW_DEVICE, self._hardware_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
|
# Bind all media mounts to directories in media
|
||||||
if self.media_mounts:
|
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:
|
async def reload(self) -> None:
|
||||||
"""Update mounts info via dbus and reload failed mounts."""
|
"""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
|
# Try to reload any newly failed mounts and report issues if failure persists
|
||||||
new_failures = [
|
new_failures = [
|
||||||
|
@ -107,7 +107,9 @@ class PluginManager(CoreSysAttributes):
|
|||||||
|
|
||||||
async def repair(self) -> None:
|
async def repair(self) -> None:
|
||||||
"""Repair Supervisor plugins."""
|
"""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:
|
async def shutdown(self) -> None:
|
||||||
"""Shutdown Supervisor plugin."""
|
"""Shutdown Supervisor plugin."""
|
||||||
|
@ -83,7 +83,7 @@ class StoreManager(CoreSysAttributes, FileConfiguration):
|
|||||||
@Job(conditions=[JobCondition.SUPERVISOR_UPDATED], on_condition=StoreJobError)
|
@Job(conditions=[JobCondition.SUPERVISOR_UPDATED], on_condition=StoreJobError)
|
||||||
async def reload(self) -> None:
|
async def reload(self) -> None:
|
||||||
"""Update add-ons from repository and reload list."""
|
"""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:
|
if tasks:
|
||||||
await asyncio.wait(tasks)
|
await asyncio.wait(tasks)
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ class Updater(FileConfiguration, CoreSysAttributes):
|
|||||||
|
|
||||||
Is a coroutine.
|
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"
|
machine = self.sys_machine or "default"
|
||||||
|
|
||||||
# Get data
|
# Get data
|
||||||
|
@ -69,14 +69,14 @@ async def cas_validate(
|
|||||||
|
|
||||||
async with async_timeout.timeout(15):
|
async with async_timeout.timeout(15):
|
||||||
data, error = await proc.communicate()
|
data, error = await proc.communicate()
|
||||||
except OSError as err:
|
|
||||||
raise CodeNotaryError(
|
|
||||||
f"CodeNotary fatal error: {err!s}", _LOGGER.critical
|
|
||||||
) from err
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
raise CodeNotaryBackendError(
|
raise CodeNotaryBackendError(
|
||||||
"Timeout while processing CodeNotary", _LOGGER.warning
|
"Timeout while processing CodeNotary", _LOGGER.warning
|
||||||
) from None
|
) from None
|
||||||
|
except OSError as err:
|
||||||
|
raise CodeNotaryError(
|
||||||
|
f"CodeNotary fatal error: {err!s}", _LOGGER.critical
|
||||||
|
) from err
|
||||||
|
|
||||||
# Check if Notarized
|
# Check if Notarized
|
||||||
if proc.returncode != 0 and not data:
|
if proc.returncode != 0 and not data:
|
||||||
|
@ -18,6 +18,7 @@ from supervisor.exceptions import (
|
|||||||
DockerAPIError,
|
DockerAPIError,
|
||||||
DockerNotFound,
|
DockerNotFound,
|
||||||
)
|
)
|
||||||
|
from supervisor.plugins.dns import PluginDns
|
||||||
from supervisor.utils import check_exception_chain
|
from supervisor.utils import check_exception_chain
|
||||||
|
|
||||||
from tests.common import load_json_fixture
|
from tests.common import load_json_fixture
|
||||||
@ -164,3 +165,20 @@ async def test_addon_uninstall_removes_discovery(
|
|||||||
|
|
||||||
assert coresys.addons.installed == []
|
assert coresys.addons.installed == []
|
||||||
assert coresys.discovery.list_messages == []
|
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.homeassistant.module import HomeAssistant
|
||||||
from supervisor.store import StoreManager
|
from supervisor.store import StoreManager
|
||||||
from supervisor.store.addon import AddonStore
|
from supervisor.store.addon import AddonStore
|
||||||
|
from supervisor.store.git import GitRepo
|
||||||
from supervisor.store.repository import Repository
|
from supervisor.store.repository import Repository
|
||||||
|
|
||||||
from tests.common import load_yaml_fixture
|
from tests.common import load_yaml_fixture
|
||||||
@ -231,3 +232,14 @@ async def test_install_unavailable_addon(
|
|||||||
await coresys.addons.install("local_ssh")
|
await coresys.addons.install("local_ssh")
|
||||||
|
|
||||||
assert log in caplog.text
|
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