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

pull/126782/head
Allen Porter 2024-09-30 03:51:41 -07:00 committed by Franck Nijhof
parent 8f47b63762
commit 4e11797d72
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
2 changed files with 65 additions and 45 deletions

View File

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

View File

@ -1,5 +1,6 @@
"""A Local To-do todo platform."""
import asyncio
import datetime
import logging
@ -130,6 +131,7 @@ class LocalTodoListEntity(TodoListEntity):
"""Initialize LocalTodoListEntity."""
self._store = store
self._calendar = calendar
self._calendar_lock = asyncio.Lock()
self._attr_name = name.capitalize()
self._attr_unique_id = unique_id
@ -159,23 +161,28 @@ class LocalTodoListEntity(TodoListEntity):
async def async_create_todo_item(self, item: TodoItem) -> None:
"""Add an item to the To-do list."""
todo = _convert_item(item)
self._new_todo_store().add(todo)
await self.async_save()
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_update_ha_state(force_refresh=True)
async def async_update_todo_item(self, item: TodoItem) -> None:
"""Update an item to the To-do list."""
todo = _convert_item(item)
self._new_todo_store().edit(todo.uid, todo)
await self.async_save()
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_update_ha_state(force_refresh=True)
async def async_delete_todo_items(self, uids: list[str]) -> None:
"""Delete an item from the To-do list."""
store = self._new_todo_store()
for uid in uids:
store.delete(uid)
await self.async_save()
async with self._calendar_lock:
for uid in uids:
store.delete(uid)
await self.async_save()
await self.async_update_ha_state(force_refresh=True)
async def async_move_todo_item(
@ -184,23 +191,24 @@ class LocalTodoListEntity(TodoListEntity):
"""Re-order an item to the To-do list."""
if uid == previous_uid:
return
todos = self._calendar.todos
item_idx: dict[str, int] = {itm.uid: idx for idx, itm in enumerate(todos)}
if uid not in item_idx:
raise HomeAssistantError(
"Item '{uid}' not found in todo list {self.entity_id}"
)
if previous_uid and previous_uid not in item_idx:
raise HomeAssistantError(
"Item '{previous_uid}' not found in todo list {self.entity_id}"
)
dst_idx = item_idx[previous_uid] + 1 if previous_uid else 0
src_idx = item_idx[uid]
src_item = todos.pop(src_idx)
if dst_idx > src_idx:
dst_idx -= 1
todos.insert(dst_idx, src_item)
await self.async_save()
async with self._calendar_lock:
todos = self._calendar.todos
item_idx: dict[str, int] = {itm.uid: idx for idx, itm in enumerate(todos)}
if uid not in item_idx:
raise HomeAssistantError(
"Item '{uid}' not found in todo list {self.entity_id}"
)
if previous_uid and previous_uid not in item_idx:
raise HomeAssistantError(
"Item '{previous_uid}' not found in todo list {self.entity_id}"
)
dst_idx = item_idx[previous_uid] + 1 if previous_uid else 0
src_idx = item_idx[uid]
src_item = todos.pop(src_idx)
if dst_idx > src_idx:
dst_idx -= 1
todos.insert(dst_idx, src_item)
await self.async_save()
await self.async_update_ha_state(force_refresh=True)
async def async_save(self) -> None: