Update local_calendar/todo to avoid blocking in the event loop (#127048)

This commit is contained in:
Allen Porter 2024-09-30 03:51:41 -07:00 committed by GitHub
parent f99b7d8b78
commit 5cc8cfb209
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 45 deletions

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import asyncio
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
import logging import logging
from typing import Any from typing import Any
@ -74,6 +75,7 @@ class LocalCalendarEntity(CalendarEntity):
"""Initialize LocalCalendarEntity.""" """Initialize LocalCalendarEntity."""
self._store = store self._store = store
self._calendar = calendar self._calendar = calendar
self._calendar_lock = asyncio.Lock()
self._event: CalendarEvent | None = None self._event: CalendarEvent | None = None
self._attr_name = name self._attr_name = name
self._attr_unique_id = unique_id self._attr_unique_id = unique_id
@ -110,7 +112,9 @@ class LocalCalendarEntity(CalendarEntity):
async def async_create_event(self, **kwargs: Any) -> None: async def async_create_event(self, **kwargs: Any) -> None:
"""Add a new event to calendar.""" """Add a new event to calendar."""
event = _parse_event(kwargs) event = _parse_event(kwargs)
EventStore(self._calendar).add(event) async with self._calendar_lock:
event_store = EventStore(self._calendar)
await self.hass.async_add_executor_job(event_store.add, event)
await self._async_store() await self._async_store()
await self.async_update_ha_state(force_refresh=True) await self.async_update_ha_state(force_refresh=True)
@ -124,6 +128,7 @@ class LocalCalendarEntity(CalendarEntity):
range_value: Range = Range.NONE range_value: Range = Range.NONE
if recurrence_range == Range.THIS_AND_FUTURE: if recurrence_range == Range.THIS_AND_FUTURE:
range_value = Range.THIS_AND_FUTURE range_value = Range.THIS_AND_FUTURE
async with self._calendar_lock:
try: try:
EventStore(self._calendar).delete( EventStore(self._calendar).delete(
uid, uid,
@ -147,13 +152,20 @@ class LocalCalendarEntity(CalendarEntity):
range_value: Range = Range.NONE range_value: Range = Range.NONE
if recurrence_range == Range.THIS_AND_FUTURE: if recurrence_range == Range.THIS_AND_FUTURE:
range_value = Range.THIS_AND_FUTURE range_value = Range.THIS_AND_FUTURE
try:
EventStore(self._calendar).edit( async with self._calendar_lock:
event_store = EventStore(self._calendar)
def apply_edit() -> None:
event_store.edit(
uid, uid,
new_event, new_event,
recurrence_id=recurrence_id, recurrence_id=recurrence_id,
recurrence_range=range_value, recurrence_range=range_value,
) )
try:
await self.hass.async_add_executor_job(apply_edit)
except EventStoreError as err: except EventStoreError as err:
raise HomeAssistantError(f"Error while updating event: {err}") from err raise HomeAssistantError(f"Error while updating event: {err}") from err
await self._async_store() await self._async_store()

View File

@ -1,5 +1,6 @@
"""A Local To-do todo platform.""" """A Local To-do todo platform."""
import asyncio
import datetime import datetime
import logging import logging
@ -130,6 +131,7 @@ class LocalTodoListEntity(TodoListEntity):
"""Initialize LocalTodoListEntity.""" """Initialize LocalTodoListEntity."""
self._store = store self._store = store
self._calendar = calendar self._calendar = calendar
self._calendar_lock = asyncio.Lock()
self._attr_name = name.capitalize() self._attr_name = name.capitalize()
self._attr_unique_id = unique_id self._attr_unique_id = unique_id
@ -159,20 +161,25 @@ class LocalTodoListEntity(TodoListEntity):
async def async_create_todo_item(self, item: TodoItem) -> None: async def async_create_todo_item(self, item: TodoItem) -> None:
"""Add an item to the To-do list.""" """Add an item to the To-do list."""
todo = _convert_item(item) todo = _convert_item(item)
self._new_todo_store().add(todo) async with self._calendar_lock:
todo_store = self._new_todo_store()
await self.hass.async_add_executor_job(todo_store.add, todo)
await self.async_save() await self.async_save()
await self.async_update_ha_state(force_refresh=True) await self.async_update_ha_state(force_refresh=True)
async def async_update_todo_item(self, item: TodoItem) -> None: async def async_update_todo_item(self, item: TodoItem) -> None:
"""Update an item to the To-do list.""" """Update an item to the To-do list."""
todo = _convert_item(item) todo = _convert_item(item)
self._new_todo_store().edit(todo.uid, todo) async with self._calendar_lock:
todo_store = self._new_todo_store()
await self.hass.async_add_executor_job(todo_store.edit, todo.uid, todo)
await self.async_save() await self.async_save()
await self.async_update_ha_state(force_refresh=True) await self.async_update_ha_state(force_refresh=True)
async def async_delete_todo_items(self, uids: list[str]) -> None: async def async_delete_todo_items(self, uids: list[str]) -> None:
"""Delete an item from the To-do list.""" """Delete an item from the To-do list."""
store = self._new_todo_store() store = self._new_todo_store()
async with self._calendar_lock:
for uid in uids: for uid in uids:
store.delete(uid) store.delete(uid)
await self.async_save() await self.async_save()
@ -184,6 +191,7 @@ class LocalTodoListEntity(TodoListEntity):
"""Re-order an item to the To-do list.""" """Re-order an item to the To-do list."""
if uid == previous_uid: if uid == previous_uid:
return return
async with self._calendar_lock:
todos = self._calendar.todos todos = self._calendar.todos
item_idx: dict[str, int] = {itm.uid: idx for idx, itm in enumerate(todos)} item_idx: dict[str, int] = {itm.uid: idx for idx, itm in enumerate(todos)}
if uid not in item_idx: if uid not in item_idx: