diff --git a/.strict-typing b/.strict-typing index 4b0d03ef095b55f009fb2aa7c9d8c78b00a0828c..3e524e48345d32347ae66e51d0bf996d4dd5a429 100644 --- a/.strict-typing +++ b/.strict-typing @@ -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.* diff --git a/homeassistant/components/shopping_list/__init__.py b/homeassistant/components/shopping_list/__init__.py index e2f04b5d88062f58240bb517ae47668efbb28eb0..e030f15d26e2822060aef0f5ad2bd71873d330c3 100644 --- a/homeassistant/components/shopping_list/__init__.py +++ b/homeassistant/components/shopping_list/__init__.py @@ -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.") diff --git a/homeassistant/components/shopping_list/intent.py b/homeassistant/components/shopping_list/intent.py index d6a29eb73f364c7da5adcb3e7cc54570ecedcbf0..180007c2dfb217d7d396a8f0c71bdce46e73c46a 100644 --- a/homeassistant/components/shopping_list/intent.py +++ b/homeassistant/components/shopping_list/intent.py @@ -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() diff --git a/mypy.ini b/mypy.ini index b92bac5e1f71b9dd048a84064a85a880cabc307a..5045659cabdde6f91d9c9ceed49f0b4ae5678781 100644 --- a/mypy.ini +++ b/mypy.ini @@ -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