Enable strict typing for shopping_list (#107913)
This commit is contained in:
parent
d4cb055d75
commit
88d7fc87c9
4 changed files with 53 additions and 22 deletions
|
@ -362,6 +362,7 @@ homeassistant.components.sensor.*
|
||||||
homeassistant.components.senz.*
|
homeassistant.components.senz.*
|
||||||
homeassistant.components.sfr_box.*
|
homeassistant.components.sfr_box.*
|
||||||
homeassistant.components.shelly.*
|
homeassistant.components.shelly.*
|
||||||
|
homeassistant.components.shopping_list.*
|
||||||
homeassistant.components.simplepush.*
|
homeassistant.components.simplepush.*
|
||||||
homeassistant.components.simplisafe.*
|
homeassistant.components.simplisafe.*
|
||||||
homeassistant.components.siren.*
|
homeassistant.components.siren.*
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
"""Support to manage a shopping list."""
|
"""Support to manage a shopping list."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
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.components.http.data_validator import RequestDataValidator
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_NAME, Platform
|
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
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.json import save_json
|
from homeassistant.helpers.json import save_json
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
@ -197,9 +200,15 @@ class ShoppingData:
|
||||||
self.items: list[dict[str, JsonValueType]] = []
|
self.items: list[dict[str, JsonValueType]] = []
|
||||||
self._listeners: list[Callable[[], None]] = []
|
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."""
|
"""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)
|
self.items.append(item)
|
||||||
await self.hass.async_add_executor_job(self.save)
|
await self.hass.async_add_executor_job(self.save)
|
||||||
self._async_notify()
|
self._async_notify()
|
||||||
|
@ -211,7 +220,7 @@ class ShoppingData:
|
||||||
return item
|
return item
|
||||||
|
|
||||||
async def async_remove(
|
async def async_remove(
|
||||||
self, item_id: str, context=None
|
self, item_id: str, context: Context | None = None
|
||||||
) -> dict[str, JsonValueType] | None:
|
) -> dict[str, JsonValueType] | None:
|
||||||
"""Remove a shopping list item."""
|
"""Remove a shopping list item."""
|
||||||
removed = await self.async_remove_items(
|
removed = await self.async_remove_items(
|
||||||
|
@ -220,7 +229,7 @@ class ShoppingData:
|
||||||
return next(iter(removed), None)
|
return next(iter(removed), None)
|
||||||
|
|
||||||
async def async_remove_items(
|
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]]:
|
) -> list[dict[str, JsonValueType]]:
|
||||||
"""Remove a shopping list item."""
|
"""Remove a shopping list item."""
|
||||||
items_dict: dict[str, dict[str, JsonValueType]] = {}
|
items_dict: dict[str, dict[str, JsonValueType]] = {}
|
||||||
|
@ -248,7 +257,9 @@ class ShoppingData:
|
||||||
)
|
)
|
||||||
return removed
|
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."""
|
"""Update a shopping list item."""
|
||||||
item = next((itm for itm in self.items if itm["id"] == item_id), None)
|
item = next((itm for itm in self.items if itm["id"] == item_id), None)
|
||||||
|
|
||||||
|
@ -266,7 +277,7 @@ class ShoppingData:
|
||||||
)
|
)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
async def async_clear_completed(self, context=None):
|
async def async_clear_completed(self, context: Context | None = None) -> None:
|
||||||
"""Clear completed items."""
|
"""Clear completed items."""
|
||||||
self.items = [itm for itm in self.items if not itm["complete"]]
|
self.items = [itm for itm in self.items if not itm["complete"]]
|
||||||
await self.hass.async_add_executor_job(self.save)
|
await self.hass.async_add_executor_job(self.save)
|
||||||
|
@ -277,7 +288,9 @@ class ShoppingData:
|
||||||
context=context,
|
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."""
|
"""Update all items in the list."""
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
item.update(info)
|
item.update(info)
|
||||||
|
@ -291,7 +304,9 @@ class ShoppingData:
|
||||||
return self.items
|
return self.items
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_reorder(self, item_ids, context=None):
|
def async_reorder(
|
||||||
|
self, item_ids: list[str], context: Context | None = None
|
||||||
|
) -> None:
|
||||||
"""Reorder items."""
|
"""Reorder items."""
|
||||||
# The array for sorted items.
|
# The array for sorted items.
|
||||||
new_items = []
|
new_items = []
|
||||||
|
@ -346,9 +361,11 @@ class ShoppingData:
|
||||||
{"action": "reorder"},
|
{"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."""
|
"""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.hass.async_add_executor_job(self.save)
|
||||||
self._async_notify()
|
self._async_notify()
|
||||||
self.hass.bus.async_fire(
|
self.hass.bus.async_fire(
|
||||||
|
@ -376,7 +393,7 @@ class ShoppingData:
|
||||||
def async_add_listener(self, cb: Callable[[], None]) -> Callable[[], None]:
|
def async_add_listener(self, cb: Callable[[], None]) -> Callable[[], None]:
|
||||||
"""Add a listener to notify when data is updated."""
|
"""Add a listener to notify when data is updated."""
|
||||||
|
|
||||||
def unsub():
|
def unsub() -> None:
|
||||||
self._listeners.remove(cb)
|
self._listeners.remove(cb)
|
||||||
|
|
||||||
self._listeners.append(cb)
|
self._listeners.append(cb)
|
||||||
|
@ -395,7 +412,7 @@ class ShoppingListView(http.HomeAssistantView):
|
||||||
name = "api:shopping_list"
|
name = "api:shopping_list"
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def get(self, request):
|
def get(self, request: web.Request) -> web.Response:
|
||||||
"""Retrieve shopping list items."""
|
"""Retrieve shopping list items."""
|
||||||
return self.json(request.app["hass"].data[DOMAIN].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}"
|
url = "/api/shopping_list/item/{item_id}"
|
||||||
name = "api:shopping_list: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."""
|
"""Update a shopping list item."""
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
|
hass: HomeAssistant = request.app["hass"]
|
||||||
|
|
||||||
try:
|
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)
|
return self.json(item)
|
||||||
except NoMatchingShoppingListItem:
|
except NoMatchingShoppingListItem:
|
||||||
return self.json_message("Item not found", HTTPStatus.NOT_FOUND)
|
return self.json_message("Item not found", HTTPStatus.NOT_FOUND)
|
||||||
|
@ -426,9 +444,10 @@ class CreateShoppingListItemView(http.HomeAssistantView):
|
||||||
name = "api:shopping_list:item"
|
name = "api:shopping_list:item"
|
||||||
|
|
||||||
@RequestDataValidator(vol.Schema({vol.Required("name"): str}))
|
@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."""
|
"""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)
|
return self.json(item)
|
||||||
|
|
||||||
|
|
||||||
|
@ -438,9 +457,9 @@ class ClearCompletedItemsView(http.HomeAssistantView):
|
||||||
url = "/api/shopping_list/clear_completed"
|
url = "/api/shopping_list/clear_completed"
|
||||||
name = "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."""
|
"""Retrieve if API is running."""
|
||||||
hass = request.app["hass"]
|
hass: HomeAssistant = request.app["hass"]
|
||||||
await hass.data[DOMAIN].async_clear_completed()
|
await hass.data[DOMAIN].async_clear_completed()
|
||||||
return self.json_message("Cleared completed items.")
|
return self.json_message("Cleared completed items.")
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Intents for the Shopping List integration."""
|
"""Intents for the Shopping List integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import intent
|
from homeassistant.helpers import intent
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ INTENT_ADD_ITEM = "HassShoppingListAddItem"
|
||||||
INTENT_LAST_ITEMS = "HassShoppingListLastItems"
|
INTENT_LAST_ITEMS = "HassShoppingListLastItems"
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_intents(hass):
|
async def async_setup_intents(hass: HomeAssistant) -> None:
|
||||||
"""Set up the Shopping List intents."""
|
"""Set up the Shopping List intents."""
|
||||||
intent.async_register(hass, AddItemIntent())
|
intent.async_register(hass, AddItemIntent())
|
||||||
intent.async_register(hass, ListTopItemsIntent())
|
intent.async_register(hass, ListTopItemsIntent())
|
||||||
|
@ -22,7 +23,7 @@ class AddItemIntent(intent.IntentHandler):
|
||||||
intent_type = INTENT_ADD_ITEM
|
intent_type = INTENT_ADD_ITEM
|
||||||
slot_schema = {"item": cv.string}
|
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."""
|
"""Handle the intent."""
|
||||||
slots = self.async_validate_slots(intent_obj.slots)
|
slots = self.async_validate_slots(intent_obj.slots)
|
||||||
item = slots["item"]["value"]
|
item = slots["item"]["value"]
|
||||||
|
@ -39,7 +40,7 @@ class ListTopItemsIntent(intent.IntentHandler):
|
||||||
intent_type = INTENT_LAST_ITEMS
|
intent_type = INTENT_LAST_ITEMS
|
||||||
slot_schema = {"item": cv.string}
|
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."""
|
"""Handle the intent."""
|
||||||
items = intent_obj.hass.data[DOMAIN].items[-5:]
|
items = intent_obj.hass.data[DOMAIN].items[-5:]
|
||||||
response = intent_obj.create_response()
|
response = intent_obj.create_response()
|
||||||
|
|
10
mypy.ini
10
mypy.ini
|
@ -3381,6 +3381,16 @@ disallow_untyped_defs = true
|
||||||
warn_return_any = true
|
warn_return_any = true
|
||||||
warn_unreachable = 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.*]
|
[mypy-homeassistant.components.simplepush.*]
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
disallow_incomplete_defs = true
|
disallow_incomplete_defs = true
|
||||||
|
|
Loading…
Add table
Reference in a new issue