Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable strict typing for onboarding #108097

Merged
merged 1 commit into from Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .strict-typing
Expand Up @@ -304,6 +304,7 @@ homeassistant.components.notify.*
homeassistant.components.notion.*
homeassistant.components.number.*
homeassistant.components.nut.*
homeassistant.components.onboarding.*
homeassistant.components.oncue.*
homeassistant.components.onewire.*
homeassistant.components.open_meteo.*
Expand Down
12 changes: 10 additions & 2 deletions homeassistant/components/onboarding/__init__.py
@@ -1,4 +1,6 @@
"""Support to help onboard new users."""
from __future__ import annotations

from typing import TYPE_CHECKING

from homeassistant.core import HomeAssistant, callback
Expand All @@ -23,10 +25,15 @@
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)


class OnboadingStorage(Store):
class OnboadingStorage(Store[dict[str, list[str]]]):
"""Store onboarding data."""

async def _async_migrate_func(self, old_major_version, old_minor_version, old_data):
async def _async_migrate_func(
self,
old_major_version: int,
old_minor_version: int,
old_data: dict[str, list[str]],
) -> dict[str, list[str]]:
"""Migrate to the new version."""
# From version 1 -> 2, we automatically mark the integration step done
if old_major_version < 2:
Expand Down Expand Up @@ -56,6 +63,7 @@ def async_is_user_onboarded(hass: HomeAssistant) -> bool:
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the onboarding component."""
store = OnboadingStorage(hass, STORAGE_VERSION, STORAGE_KEY, private=True)
data: dict[str, list[str]] | None
if (data := await store.async_load()) is None:
data = {"done": []}

Expand Down
42 changes: 24 additions & 18 deletions homeassistant/components/onboarding/views.py
Expand Up @@ -3,7 +3,7 @@

import asyncio
from http import HTTPStatus
from typing import cast
from typing import TYPE_CHECKING, Any, cast

from aiohttp import web
from aiohttp.web_exceptions import HTTPUnauthorized
Expand All @@ -21,6 +21,9 @@
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.helpers.translation import async_get_translations

if TYPE_CHECKING:
from . import OnboadingStorage

from .const import (
DEFAULT_AREAS,
DOMAIN,
Expand All @@ -32,7 +35,9 @@
)


async def async_setup(hass, data, store):
async def async_setup(
hass: HomeAssistant, data: dict[str, list[str]], store: OnboadingStorage
) -> None:
"""Set up the onboarding view."""
hass.http.register_view(OnboardingView(data, store))
hass.http.register_view(InstallationTypeOnboardingView(data))
Expand All @@ -49,12 +54,12 @@ class OnboardingView(HomeAssistantView):
url = "/api/onboarding"
name = "api:onboarding"

def __init__(self, data, store):
def __init__(self, data: dict[str, list[str]], store: OnboadingStorage) -> None:
"""Initialize the onboarding view."""
self._store = store
self._data = data

async def get(self, request):
async def get(self, request: web.Request) -> web.Response:
"""Return the onboarding status."""
return self.json(
[{"step": key, "done": key in self._data["done"]} for key in STEPS]
Expand All @@ -68,37 +73,37 @@ class InstallationTypeOnboardingView(HomeAssistantView):
url = "/api/onboarding/installation_type"
name = "api:onboarding:installation_type"

def __init__(self, data):
def __init__(self, data: dict[str, list[str]]) -> None:
"""Initialize the onboarding installation type view."""
self._data = data

async def get(self, request):
async def get(self, request: web.Request) -> web.Response:
"""Return the onboarding status."""
if self._data["done"]:
raise HTTPUnauthorized()

hass = request.app["hass"]
hass: HomeAssistant = request.app["hass"]
info = await async_get_system_info(hass)
return self.json({"installation_type": info["installation_type"]})


class _BaseOnboardingView(HomeAssistantView):
"""Base class for onboarding."""

step: str | None = None
step: str

def __init__(self, data, store):
def __init__(self, data: dict[str, list[str]], store: OnboadingStorage) -> None:
"""Initialize the onboarding view."""
self._store = store
self._data = data
self._lock = asyncio.Lock()

@callback
def _async_is_done(self):
def _async_is_done(self) -> bool:
"""Return if this step is done."""
return self.step in self._data["done"]

async def _async_mark_done(self, hass):
async def _async_mark_done(self, hass: HomeAssistant) -> None:
"""Mark step as done."""
self._data["done"].append(self.step)
await self._store.async_save(self._data)
Expand Down Expand Up @@ -180,9 +185,9 @@ class CoreConfigOnboardingView(_BaseOnboardingView):
name = "api:onboarding:core_config"
step = STEP_CORE_CONFIG

async def post(self, request):
async def post(self, request: web.Request) -> web.Response:
"""Handle finishing core config step."""
hass = request.app["hass"]
hass: HomeAssistant = request.app["hass"]

async with self._lock:
if self._async_is_done():
Expand All @@ -205,7 +210,8 @@ async def post(self, request):

if (
hassio.is_hassio(hass)
and "raspberrypi" in hassio.get_core_info(hass)["machine"]
and (core_info := hassio.get_core_info(hass))
and "raspberrypi" in core_info["machine"]
):
onboard_integrations.append("rpi_power")

Expand All @@ -232,9 +238,9 @@ class IntegrationOnboardingView(_BaseOnboardingView):
@RequestDataValidator(
vol.Schema({vol.Required("client_id"): str, vol.Required("redirect_uri"): str})
)
async def post(self, request, data):
async def post(self, request: web.Request, data: dict[str, Any]) -> web.Response:
"""Handle token creation."""
hass = request.app["hass"]
hass: HomeAssistant = request.app["hass"]
refresh_token_id = request[KEY_HASS_REFRESH_TOKEN_ID]

async with self._lock:
Expand Down Expand Up @@ -276,9 +282,9 @@ class AnalyticsOnboardingView(_BaseOnboardingView):
name = "api:onboarding:analytics"
step = STEP_ANALYTICS

async def post(self, request):
async def post(self, request: web.Request) -> web.Response:
"""Handle finishing analytics step."""
hass = request.app["hass"]
hass: HomeAssistant = request.app["hass"]

async with self._lock:
if self._async_is_done():
Expand Down
8 changes: 7 additions & 1 deletion homeassistant/components/person/__init__.py
Expand Up @@ -92,7 +92,13 @@


@bind_hass
async def async_create_person(hass, name, *, user_id=None, device_trackers=None):
async def async_create_person(
hass: HomeAssistant,
name: str,
*,
user_id: str | None = None,
device_trackers: list[str] | None = None,
) -> None:
"""Create a new person."""
await hass.data[DOMAIN][1].async_create_item(
{
Expand Down
10 changes: 10 additions & 0 deletions mypy.ini
Expand Up @@ -2801,6 +2801,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true

[mypy-homeassistant.components.onboarding.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true

[mypy-homeassistant.components.oncue.*]
check_untyped_defs = true
disallow_incomplete_defs = true
Expand Down