Cache serialize of manifest for loaded integrations (#117965)

* Cache serialize of manifest for loaded integrations

The manifest/list and manifest/get websocket apis
are called frequently when moving around in the UI.
Since the manifest does not change we can make
the the serialized version a cached property

* reduce

* reduce
This commit is contained in:
J. Nick Koston 2024-05-23 04:52:05 -10:00 committed by GitHub
parent 09e7156d2d
commit c5cc9801a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 26 additions and 12 deletions

View file

@ -46,10 +46,10 @@ from homeassistant.helpers.json import (
ExtendedJSONEncoder,
find_paths_unserializable_data,
json_bytes,
json_fragment,
)
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.loader import (
Integration,
IntegrationNotFound,
async_get_integration,
async_get_integration_descriptions,
@ -505,19 +505,15 @@ async def handle_manifest_list(
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
) -> None:
"""Handle integrations command."""
wanted_integrations = msg.get("integrations")
if wanted_integrations is None:
wanted_integrations = async_get_loaded_integrations(hass)
ints_or_excs = await async_get_integrations(hass, wanted_integrations)
integrations: list[Integration] = []
ints_or_excs = await async_get_integrations(
hass, msg.get("integrations") or async_get_loaded_integrations(hass)
)
manifest_json_fragments: list[json_fragment] = []
for int_or_exc in ints_or_excs.values():
if isinstance(int_or_exc, Exception):
raise int_or_exc
integrations.append(int_or_exc)
connection.send_result(
msg["id"], [integration.manifest for integration in integrations]
)
manifest_json_fragments.append(int_or_exc.manifest_json_fragment)
connection.send_result(msg["id"], manifest_json_fragments)
@decorators.websocket_command(
@ -530,9 +526,10 @@ async def handle_manifest_get(
"""Handle integrations command."""
try:
integration = await async_get_integration(hass, msg["integration"])
connection.send_result(msg["id"], integration.manifest)
except IntegrationNotFound:
connection.send_error(msg["id"], const.ERR_NOT_FOUND, "Integration not found")
else:
connection.send_result(msg["id"], integration.manifest_json_fragment)
@callback

View file

@ -39,6 +39,7 @@ from .generated.mqtt import MQTT
from .generated.ssdp import SSDP
from .generated.usb import USB
from .generated.zeroconf import HOMEKIT, ZEROCONF
from .helpers.json import json_bytes, json_fragment
from .util.hass_dict import HassKey
from .util.json import JSON_DECODE_EXCEPTIONS, json_loads
@ -762,6 +763,11 @@ class Integration:
self._top_level_files = top_level_files or set()
_LOGGER.info("Loaded %s from %s", self.domain, pkg_path)
@cached_property
def manifest_json_fragment(self) -> json_fragment:
"""Return manifest as a JSON fragment."""
return json_fragment(json_bytes(self.manifest))
@cached_property
def name(self) -> str:
"""Return name."""

View file

@ -15,6 +15,8 @@ from homeassistant.components import http, hue
from homeassistant.components.hue import light as hue_light
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import frame
from homeassistant.helpers.json import json_dumps
from homeassistant.util.json import json_loads
from .common import MockModule, async_get_persistent_notifications, mock_integration
@ -1959,3 +1961,12 @@ async def test_hass_helpers_use_reported(
"Detected that custom integration 'test_integration_frame' "
"accesses hass.helpers.aiohttp_client. This is deprecated"
) in caplog.text
async def test_manifest_json_fragment_round_trip(hass: HomeAssistant) -> None:
"""Test json_fragment roundtrip."""
integration = await loader.async_get_integration(hass, "hue")
assert (
json_loads(json_dumps(integration.manifest_json_fragment))
== integration.manifest
)