Skip to content

Commit 0636e49

Browse files
authored
Enable mypy part 1 (addons and api) (#5759)
* Fix mypy issues in addons * Fix mypy issues in api * fix docstring * Brackets instead of get with default
1 parent 543d6ef commit 0636e49

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+500
-382
lines changed

supervisor/addons/addon.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from typing import Any, Final
1919

2020
import aiohttp
21-
from awesomeversion import AwesomeVersionCompareException
21+
from awesomeversion import AwesomeVersion, AwesomeVersionCompareException
2222
from deepmerge import Merger
2323
from securetar import AddFileError, atomic_contents_add, secure_path
2424
import voluptuous as vol
@@ -285,28 +285,28 @@ def is_detached(self) -> bool:
285285
@property
286286
def with_icon(self) -> bool:
287287
"""Return True if an icon exists."""
288-
if self.is_detached:
288+
if self.is_detached or not self.addon_store:
289289
return super().with_icon
290290
return self.addon_store.with_icon
291291

292292
@property
293293
def with_logo(self) -> bool:
294294
"""Return True if a logo exists."""
295-
if self.is_detached:
295+
if self.is_detached or not self.addon_store:
296296
return super().with_logo
297297
return self.addon_store.with_logo
298298

299299
@property
300300
def with_changelog(self) -> bool:
301301
"""Return True if a changelog exists."""
302-
if self.is_detached:
302+
if self.is_detached or not self.addon_store:
303303
return super().with_changelog
304304
return self.addon_store.with_changelog
305305

306306
@property
307307
def with_documentation(self) -> bool:
308308
"""Return True if a documentation exists."""
309-
if self.is_detached:
309+
if self.is_detached or not self.addon_store:
310310
return super().with_documentation
311311
return self.addon_store.with_documentation
312312

@@ -316,7 +316,7 @@ def available(self) -> bool:
316316
return self._available(self.data_store)
317317

318318
@property
319-
def version(self) -> str | None:
319+
def version(self) -> AwesomeVersion:
320320
"""Return installed version."""
321321
return self.persist[ATTR_VERSION]
322322

@@ -464,7 +464,7 @@ def ingress_entry(self) -> str | None:
464464
return None
465465

466466
@property
467-
def latest_version(self) -> str:
467+
def latest_version(self) -> AwesomeVersion:
468468
"""Return version of add-on."""
469469
return self.data_store[ATTR_VERSION]
470470

@@ -518,9 +518,8 @@ def ingress_url(self) -> str | None:
518518
def webui(self) -> str | None:
519519
"""Return URL to webui or None."""
520520
url = super().webui
521-
if not url:
521+
if not url or not (webui := RE_WEBUI.match(url)):
522522
return None
523-
webui = RE_WEBUI.match(url)
524523

525524
# extract arguments
526525
t_port = webui.group("t_port")
@@ -675,10 +674,9 @@ async def save_persist(self) -> None:
675674

676675
async def watchdog_application(self) -> bool:
677676
"""Return True if application is running."""
678-
url = super().watchdog
679-
if not url:
677+
url = self.watchdog_url
678+
if not url or not (application := RE_WATCHDOG.match(url)):
680679
return True
681-
application = RE_WATCHDOG.match(url)
682680

683681
# extract arguments
684682
t_port = int(application.group("t_port"))
@@ -687,8 +685,10 @@ async def watchdog_application(self) -> bool:
687685
s_suffix = application.group("s_suffix") or ""
688686

689687
# search host port for this docker port
690-
if self.host_network:
691-
port = self.ports.get(f"{t_port}/tcp", t_port)
688+
if self.host_network and self.ports:
689+
port = self.ports.get(f"{t_port}/tcp")
690+
if port is None:
691+
port = t_port
692692
else:
693693
port = t_port
694694

@@ -777,6 +777,9 @@ async def _check_ingress_port(self):
777777
)
778778
async def install(self) -> None:
779779
"""Install and setup this addon."""
780+
if not self.addon_store:
781+
raise AddonsError("Missing from store, cannot install!")
782+
780783
await self.sys_addons.data.install(self.addon_store)
781784
await self.load()
782785

@@ -880,6 +883,9 @@ async def update(self) -> asyncio.Task | None:
880883
Returns a Task that completes when addon has state 'started' (see start)
881884
if it was running. Else nothing is returned.
882885
"""
886+
if not self.addon_store:
887+
raise AddonsError("Missing from store, cannot update!")
888+
883889
old_image = self.image
884890
# Cache data to prevent races with other updates to global
885891
store = self.addon_store.clone()
@@ -936,7 +942,9 @@ async def rebuild(self) -> asyncio.Task | None:
936942
except DockerError as err:
937943
raise AddonsError() from err
938944

939-
await self.sys_addons.data.update(self.addon_store)
945+
if self.addon_store:
946+
await self.sys_addons.data.update(self.addon_store)
947+
940948
await self._check_ingress_port()
941949
_LOGGER.info("Add-on '%s' successfully rebuilt", self.slug)
942950

@@ -965,7 +973,9 @@ def write_pulse_config():
965973
await self.sys_run_in_executor(write_pulse_config)
966974
except OSError as err:
967975
if err.errno == errno.EBADMSG:
968-
self.sys_resolution.unhealthy = UnhealthyReason.OSERROR_BAD_MESSAGE
976+
self.sys_resolution.add_unhealthy_reason(
977+
UnhealthyReason.OSERROR_BAD_MESSAGE
978+
)
969979
_LOGGER.error(
970980
"Add-on %s can't write pulse/client.config: %s", self.slug, err
971981
)
@@ -1324,7 +1334,7 @@ def _addon_backup(
13241334
arcname="config",
13251335
)
13261336

1327-
wait_for_start: Awaitable[None] | None = None
1337+
wait_for_start: asyncio.Task | None = None
13281338

13291339
data = {
13301340
ATTR_USER: self.persist,
@@ -1370,7 +1380,7 @@ async def restore(self, tar_file: tarfile.TarFile) -> asyncio.Task | None:
13701380
Returns a Task that completes when addon has state 'started' (see start)
13711381
if addon is started after restore. Else nothing is returned.
13721382
"""
1373-
wait_for_start: Awaitable[None] | None = None
1383+
wait_for_start: asyncio.Task | None = None
13741384

13751385
# Extract backup
13761386
def _extract_tarfile() -> tuple[TemporaryDirectory, dict[str, Any]]:
@@ -1594,6 +1604,6 @@ async def watchdog_container(self, event: DockerContainerStateEvent) -> None:
15941604

15951605
def refresh_path_cache(self) -> Awaitable[None]:
15961606
"""Refresh cache of existing paths."""
1597-
if self.is_detached:
1607+
if self.is_detached or not self.addon_store:
15981608
return super().refresh_path_cache()
15991609
return self.addon_store.refresh_path_cache()

supervisor/addons/build.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from functools import cached_property
66
from pathlib import Path
7-
from typing import TYPE_CHECKING
7+
from typing import TYPE_CHECKING, Any
88

99
from awesomeversion import AwesomeVersion
1010

@@ -23,7 +23,7 @@
2323
from .validate import SCHEMA_BUILD_CONFIG
2424

2525
if TYPE_CHECKING:
26-
from . import AnyAddon
26+
from .manager import AnyAddon
2727

2828

2929
class AddonBuild(FileConfiguration, CoreSysAttributes):
@@ -63,7 +63,7 @@ async def save_data(self):
6363
@cached_property
6464
def arch(self) -> str:
6565
"""Return arch of the add-on."""
66-
return self.sys_arch.match(self.addon.arch)
66+
return self.sys_arch.match([self.addon.arch])
6767

6868
@property
6969
def base_image(self) -> str:
@@ -126,7 +126,7 @@ def get_docker_args(self, version: AwesomeVersion, image: str | None = None):
126126
127127
Must be run in executor.
128128
"""
129-
args = {
129+
args: dict[str, Any] = {
130130
"path": str(self.addon.path_location),
131131
"tag": f"{image or self.addon.image}:{version!s}",
132132
"dockerfile": str(self.get_dockerfile()),

supervisor/addons/manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ async def install(self, slug: str) -> None:
194194

195195
_LOGGER.info("Add-on '%s' successfully installed", slug)
196196

197+
@Job(name="addon_manager_uninstall")
197198
async def uninstall(self, slug: str, *, remove_config: bool = False) -> None:
198199
"""Remove an add-on."""
199200
if slug not in self.local:
@@ -313,7 +314,7 @@ async def restore(
313314
if slug not in self.local:
314315
_LOGGER.debug("Add-on %s is not local available for restore", slug)
315316
addon = Addon(self.coresys, slug)
316-
had_ingress = False
317+
had_ingress: bool | None = False
317318
else:
318319
_LOGGER.debug("Add-on %s is local available for restore", slug)
319320
addon = self.local[slug]

supervisor/addons/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def webui(self) -> str | None:
294294
return self.data.get(ATTR_WEBUI)
295295

296296
@property
297-
def watchdog(self) -> str | None:
297+
def watchdog_url(self) -> str | None:
298298
"""Return URL to for watchdog or None."""
299299
return self.data.get(ATTR_WATCHDOG)
300300

@@ -606,7 +606,7 @@ def schema(self) -> AddonOptions:
606606
return AddonOptions(self.coresys, raw_schema, self.name, self.slug)
607607

608608
@property
609-
def schema_ui(self) -> list[dict[any, any]] | None:
609+
def schema_ui(self) -> list[dict[Any, Any]] | None:
610610
"""Create a UI schema for add-on options."""
611611
raw_schema = self.data[ATTR_SCHEMA]
612612

supervisor/addons/options.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def _single_validate(self, typ: str, value: Any, key: str):
137137
) from None
138138

139139
# prepare range
140-
range_args = {}
140+
range_args: dict[str, Any] = {}
141141
for group_name in _SCHEMA_LENGTH_PARTS:
142142
group_value = match.group(group_name)
143143
if group_value:
@@ -390,14 +390,14 @@ def _nested_ui_dict(
390390
multiple: bool = False,
391391
) -> None:
392392
"""UI nested dict items."""
393-
ui_node = {
393+
ui_node: dict[str, Any] = {
394394
"name": key,
395395
"type": "schema",
396396
"optional": True,
397397
"multiple": multiple,
398398
}
399399

400-
nested_schema = []
400+
nested_schema: list[dict[str, Any]] = []
401401
for c_key, c_value in option_dict.items():
402402
# Nested?
403403
if isinstance(c_value, list):
@@ -413,7 +413,7 @@ def _create_device_filter(str_filter: str) -> dict[str, Any]:
413413
"""Generate device Filter."""
414414
raw_filter = dict(value.split("=") for value in str_filter.split(";"))
415415

416-
clean_filter = {}
416+
clean_filter: dict[str, Any] = {}
417417
for key, value in raw_filter.items():
418418
if key == "subsystem":
419419
clean_filter[key] = UdevSubsystem(value)

supervisor/api/__init__.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pathlib import Path
77
from typing import Any
88

9-
from aiohttp import web
9+
from aiohttp import hdrs, web
1010

1111
from ..const import AddonState
1212
from ..coresys import CoreSys, CoreSysAttributes
@@ -82,15 +82,13 @@ def __init__(self, coresys: CoreSys):
8282
self._site: web.TCPSite | None = None
8383

8484
# share single host API handler for reuse in logging endpoints
85-
self._api_host: APIHost | None = None
85+
self._api_host: APIHost = APIHost()
86+
self._api_host.coresys = coresys
8687

8788
async def load(self) -> None:
8889
"""Register REST API Calls."""
8990
static_resource_configs: list[StaticResourceConfig] = []
9091

91-
self._api_host = APIHost()
92-
self._api_host.coresys = self.coresys
93-
9492
self._register_addons()
9593
self._register_audio()
9694
self._register_auth()
@@ -526,7 +524,7 @@ def _register_addons(self) -> None:
526524

527525
self.webapp.add_routes(
528526
[
529-
web.get("/addons", api_addons.list),
527+
web.get("/addons", api_addons.list_addons),
530528
web.post("/addons/{addon}/uninstall", api_addons.uninstall),
531529
web.post("/addons/{addon}/start", api_addons.start),
532530
web.post("/addons/{addon}/stop", api_addons.stop),
@@ -594,7 +592,9 @@ def _register_ingress(self) -> None:
594592
web.post("/ingress/session", api_ingress.create_session),
595593
web.post("/ingress/validate_session", api_ingress.validate_session),
596594
web.get("/ingress/panels", api_ingress.panels),
597-
web.view("/ingress/{token}/{path:.*}", api_ingress.handler),
595+
web.route(
596+
hdrs.METH_ANY, "/ingress/{token}/{path:.*}", api_ingress.handler
597+
),
598598
]
599599
)
600600

@@ -605,7 +605,7 @@ def _register_backups(self) -> None:
605605

606606
self.webapp.add_routes(
607607
[
608-
web.get("/backups", api_backups.list),
608+
web.get("/backups", api_backups.list_backups),
609609
web.get("/backups/info", api_backups.info),
610610
web.post("/backups/options", api_backups.options),
611611
web.post("/backups/reload", api_backups.reload),
@@ -632,7 +632,7 @@ def _register_services(self) -> None:
632632

633633
self.webapp.add_routes(
634634
[
635-
web.get("/services", api_services.list),
635+
web.get("/services", api_services.list_services),
636636
web.get("/services/{service}", api_services.get_service),
637637
web.post("/services/{service}", api_services.set_service),
638638
web.delete("/services/{service}", api_services.del_service),
@@ -646,7 +646,7 @@ def _register_discovery(self) -> None:
646646

647647
self.webapp.add_routes(
648648
[
649-
web.get("/discovery", api_discovery.list),
649+
web.get("/discovery", api_discovery.list_discovery),
650650
web.get("/discovery/{uuid}", api_discovery.get_discovery),
651651
web.delete("/discovery/{uuid}", api_discovery.del_discovery),
652652
web.post("/discovery", api_discovery.set_discovery),

0 commit comments

Comments
 (0)