60 lines
1.6 KiB
Python
60 lines
1.6 KiB
Python
"""Helper to help coordinating calls."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import functools
|
|
from typing import Callable, TypeVar, cast
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.loader import bind_hass
|
|
|
|
T = TypeVar("T")
|
|
|
|
FUNC = Callable[[HomeAssistant], T]
|
|
|
|
|
|
def singleton(data_key: str) -> Callable[[FUNC], FUNC]:
|
|
"""Decorate a function that should be called once per instance.
|
|
|
|
Result will be cached and simultaneous calls will be handled.
|
|
"""
|
|
|
|
def wrapper(func: FUNC) -> FUNC:
|
|
"""Wrap a function with caching logic."""
|
|
if not asyncio.iscoroutinefunction(func):
|
|
|
|
@bind_hass
|
|
@functools.wraps(func)
|
|
def wrapped(hass: HomeAssistant) -> T:
|
|
obj: T | None = hass.data.get(data_key)
|
|
if obj is None:
|
|
obj = hass.data[data_key] = func(hass)
|
|
return obj
|
|
|
|
return wrapped
|
|
|
|
@bind_hass
|
|
@functools.wraps(func)
|
|
async def async_wrapped(hass: HomeAssistant) -> T:
|
|
obj_or_evt = hass.data.get(data_key)
|
|
|
|
if not obj_or_evt:
|
|
evt = hass.data[data_key] = asyncio.Event()
|
|
|
|
result = await func(hass)
|
|
|
|
hass.data[data_key] = result
|
|
evt.set()
|
|
return cast(T, result)
|
|
|
|
if isinstance(obj_or_evt, asyncio.Event):
|
|
evt = obj_or_evt
|
|
await evt.wait()
|
|
return cast(T, hass.data.get(data_key))
|
|
|
|
return cast(T, obj_or_evt)
|
|
|
|
return async_wrapped
|
|
|
|
return wrapper
|