Enable strict typing for shopping_list (#107913)

This commit is contained in:
Marc Mueller 2024-01-14 09:38:53 +01:00 committed by GitHub
parent d4cb055d75
commit 88d7fc87c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 22 deletions

View File

@ -362,6 +362,7 @@ homeassistant.components.sensor.*
homeassistant.components.senz.*
homeassistant.components.sfr_box.*
homeassistant.components.shelly.*
homeassistant.components.shopping_list.*
homeassistant.components.simplepush.*
homeassistant.components.simplisafe.*
homeassistant.components.siren.*

View File

@ -1,10 +1,13 @@
"""Support to manage a shopping list."""
from __future__ import annotations
from collections.abc import Callable
from http import HTTPStatus
import logging
from typing import Any, cast
import uuid
from aiohttp import web
import voluptuous as vol
from homeassistant import config_entries
@ -12,7 +15,7 @@ from homeassistant.components import http, websocket_api
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_NAME, Platform
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.core import Context, HomeAssistant, ServiceCall, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.json import save_json
from homeassistant.helpers.typing import ConfigType
@ -197,9 +200,15 @@ class ShoppingData:
self.items: list[dict[str, JsonValueType]] = []
self._listeners: list[Callable[[], None]] = []
async def async_add(self, name, complete=False, context=None):
async def async_add(
self, name: str | None, complete: bool = False, context: Context | None = None
) -> dict[str, JsonValueType]:
"""Add a shopping list item."""
item = {"name": name, "id": uuid.uuid4().hex, "complete": complete}
item: dict[str, JsonValueType] = {
"name": name,
"id": uuid.uuid4().hex,
"complete": complete,
}
self.items.append(item)
await self.hass.async_add_executor_job(self.save)
self._async_notify()
@ -211,7 +220,7 @@ class ShoppingData:
return item
async def async_remove(
self, item_id: str, context=None
self, item_id: str, context: Context | None = None
) -> dict[str, JsonValueType] | None:
"""Remove a shopping list item."""
removed = await self.async_remove_items(
@ -220,7 +229,7 @@ class ShoppingData:
return next(iter(removed), None)
async def async_remove_items(
self, item_ids: set[str], context=None
self, item_ids: set[str], context: Context | None = None
) -> list[dict[str, JsonValueType]]:
"""Remove a shopping list item."""
items_dict: dict[str, dict[str, JsonValueType]] = {}
@ -248,7 +257,9 @@ class ShoppingData:
)
return removed
async def async_update(self, item_id, info, context=None):
async def async_update(
self, item_id: str | None, info: dict[str, Any], context: Context | None = None
) -> dict[str, JsonValueType]:
"""Update a shopping list item."""
item = next((itm for itm in self.items if itm["id"] == item_id), None)
@ -266,7 +277,7 @@ class ShoppingData:
)
return item
async def async_clear_completed(self, context=None):
async def async_clear_completed(self, context: Context | None = None) -> None:
"""Clear completed items."""
self.items = [itm for itm in self.items if not itm["complete"]]
await self.hass.async_add_executor_job(self.save)
@ -277,7 +288,9 @@ class ShoppingData:
context=context,
)
async def async_update_list(self, info, context=None):
async def async_update_list(
self, info: dict[str, JsonValueType], context: Context | None = None
) -> list[dict[str, JsonValueType]]:
"""Update all items in the list."""
for item in self.items:
item.update(info)
@ -291,7 +304,9 @@ class ShoppingData:
return self.items
@callback
def async_reorder(self, item_ids, context=None):
def async_reorder(
self, item_ids: list[str], context: Context | None = None
) -> None:
"""Reorder items."""
# The array for sorted items.
new_items = []
@ -346,9 +361,11 @@ class ShoppingData:
{"action": "reorder"},
)
async def async_sort(self, reverse=False, context=None):
async def async_sort(
self, reverse: bool = False, context: Context | None = None
) -> None:
"""Sort items by name."""
self.items = sorted(self.items, key=lambda item: item["name"], reverse=reverse)
self.items = sorted(self.items, key=lambda item: item["name"], reverse=reverse) # type: ignore[arg-type,return-value]
self.hass.async_add_executor_job(self.save)
self._async_notify()
self.hass.bus.async_fire(
@ -376,7 +393,7 @@ class ShoppingData:
def async_add_listener(self, cb: Callable[[], None]) -> Callable[[], None]:
"""Add a listener to notify when data is updated."""
def unsub():
def unsub() -> None:
self._listeners.remove(cb)
self._listeners.append(cb)
@ -395,7 +412,7 @@ class ShoppingListView(http.HomeAssistantView):
name = "api:shopping_list"
@callback
def get(self, request):
def get(self, request: web.Request) -> web.Response:
"""Retrieve shopping list items."""
return self.json(request.app["hass"].data[DOMAIN].items)
@ -406,12 +423,13 @@ class UpdateShoppingListItemView(http.HomeAssistantView):
url = "/api/shopping_list/item/{item_id}"
name = "api:shopping_list:item:id"
async def post(self, request, item_id):
async def post(self, request: web.Request, item_id: str) -> web.Response:
"""Update a shopping list item."""
data = await request.json()
hass: HomeAssistant = request.app["hass"]
try:
item = await request.app["hass"].data[DOMAIN].async_update(item_id, data)
item = await hass.data[DOMAIN].async_update(item_id, data)
return self.json(item)
except NoMatchingShoppingListItem:
return self.json_message("Item not found", HTTPStatus.NOT_FOUND)
@ -426,9 +444,10 @@ class CreateShoppingListItemView(http.HomeAssistantView):
name = "api:shopping_list:item"
@RequestDataValidator(vol.Schema({vol.Required("name"): str}))
async def post(self, request, data):
async def post(self, request: web.Request, data: dict[str, str]) -> web.Response:
"""Create a new shopping list item."""
item = await request.app["hass"].data[DOMAIN].async_add(data["name"])
hass: HomeAssistant = request.app["hass"]
item = await hass.data[DOMAIN].async_add(data["name"])
return self.json(item)
@ -438,9 +457,9 @@ class ClearCompletedItemsView(http.HomeAssistantView):
url = "/api/shopping_list/clear_completed"
name = "api:shopping_list:clear_completed"
async def post(self, request):
async def post(self, request: web.Request) -> web.Response:
"""Retrieve if API is running."""
hass = request.app["hass"]
hass: HomeAssistant = request.app["hass"]
await hass.data[DOMAIN].async_clear_completed()
return self.json_message("Cleared completed items.")

View File

@ -1,6 +1,7 @@
"""Intents for the Shopping List integration."""
from __future__ import annotations
from homeassistant.core import HomeAssistant
from homeassistant.helpers import intent
import homeassistant.helpers.config_validation as cv
@ -10,7 +11,7 @@ INTENT_ADD_ITEM = "HassShoppingListAddItem"
INTENT_LAST_ITEMS = "HassShoppingListLastItems"
async def async_setup_intents(hass):
async def async_setup_intents(hass: HomeAssistant) -> None:
"""Set up the Shopping List intents."""
intent.async_register(hass, AddItemIntent())
intent.async_register(hass, ListTopItemsIntent())
@ -22,7 +23,7 @@ class AddItemIntent(intent.IntentHandler):
intent_type = INTENT_ADD_ITEM
slot_schema = {"item": cv.string}
async def async_handle(self, intent_obj: intent.Intent):
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
"""Handle the intent."""
slots = self.async_validate_slots(intent_obj.slots)
item = slots["item"]["value"]
@ -39,7 +40,7 @@ class ListTopItemsIntent(intent.IntentHandler):
intent_type = INTENT_LAST_ITEMS
slot_schema = {"item": cv.string}
async def async_handle(self, intent_obj: intent.Intent):
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
"""Handle the intent."""
items = intent_obj.hass.data[DOMAIN].items[-5:]
response = intent_obj.create_response()

View File

@ -3381,6 +3381,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.shopping_list.*]
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.simplepush.*]
check_untyped_defs = true
disallow_incomplete_defs = true