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

View file

@ -39,6 +39,7 @@ from .generated.mqtt import MQTT
from .generated.ssdp import SSDP from .generated.ssdp import SSDP
from .generated.usb import USB from .generated.usb import USB
from .generated.zeroconf import HOMEKIT, ZEROCONF from .generated.zeroconf import HOMEKIT, ZEROCONF
from .helpers.json import json_bytes, json_fragment
from .util.hass_dict import HassKey from .util.hass_dict import HassKey
from .util.json import JSON_DECODE_EXCEPTIONS, json_loads from .util.json import JSON_DECODE_EXCEPTIONS, json_loads
@ -762,6 +763,11 @@ class Integration:
self._top_level_files = top_level_files or set() self._top_level_files = top_level_files or set()
_LOGGER.info("Loaded %s from %s", self.domain, pkg_path) _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 @cached_property
def name(self) -> str: def name(self) -> str:
"""Return name.""" """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.components.hue import light as hue_light
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import frame 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 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' " "Detected that custom integration 'test_integration_frame' "
"accesses hass.helpers.aiohttp_client. This is deprecated" "accesses hass.helpers.aiohttp_client. This is deprecated"
) in caplog.text ) 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
)