mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 01:11:51 -04:00
Trim cached orjson fragments kept by non core containers (#181511)
This commit is contained in:
@@ -15,7 +15,7 @@ from homeassistant.const import CONF_FILENAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import collection, storage
|
||||
from homeassistant.helpers.json import json_bytes, json_fragment
|
||||
from homeassistant.helpers.json import cached_json_fragment, json_fragment
|
||||
from homeassistant.util.yaml import Secrets, load_yaml_dict
|
||||
|
||||
from .const import (
|
||||
@@ -182,7 +182,7 @@ class LovelaceStorage(LovelaceConfig):
|
||||
"""Build JSON representation of the config."""
|
||||
if self._data is None or self._data["config"] is None:
|
||||
raise ConfigNotFound
|
||||
self._json_config = json_fragment(json_bytes(self._data["config"]))
|
||||
self._json_config = cached_json_fragment(self._data["config"])
|
||||
return self._json_config
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ class LovelaceYAML(LovelaceConfig):
|
||||
except FileNotFoundError:
|
||||
raise ConfigNotFound from None
|
||||
|
||||
json = json_fragment(json_bytes(config))
|
||||
json = cached_json_fragment(config)
|
||||
self._cache = (config, time.time(), json)
|
||||
return is_updated, config, json
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ from homeassistant.helpers.event import (
|
||||
from homeassistant.helpers.json import (
|
||||
JSON_DUMP,
|
||||
ExtendedJSONEncoder,
|
||||
cached_json_bytes,
|
||||
find_paths_unserializable_data,
|
||||
json_bytes,
|
||||
json_fragment,
|
||||
)
|
||||
from homeassistant.helpers.service import (
|
||||
@@ -541,7 +541,7 @@ async def _async_get_all_condition_descriptions_json(hass: HomeAssistant) -> byt
|
||||
# If the descriptions are the same, return the cached JSON payload
|
||||
if cached_descriptions is descriptions:
|
||||
return cast(bytes, cached_json_payload)
|
||||
json_payload = json_bytes(
|
||||
json_payload = cached_json_bytes(
|
||||
{
|
||||
condition: description
|
||||
for condition, description in descriptions.items()
|
||||
@@ -588,7 +588,7 @@ async def _async_get_all_service_descriptions_json(hass: HomeAssistant) -> bytes
|
||||
# If the descriptions are the same, return the cached JSON payload
|
||||
if cached_descriptions is descriptions:
|
||||
return cast(bytes, cached_json_payload)
|
||||
json_payload = json_bytes(descriptions)
|
||||
json_payload = cached_json_bytes(descriptions)
|
||||
hass.data[ALL_SERVICE_DESCRIPTIONS_JSON_CACHE] = (descriptions, json_payload)
|
||||
return json_payload
|
||||
|
||||
@@ -613,7 +613,7 @@ async def _async_get_all_trigger_descriptions_json(hass: HomeAssistant) -> bytes
|
||||
# If the descriptions are the same, return the cached JSON payload
|
||||
if cached_descriptions is descriptions:
|
||||
return cast(bytes, cached_json_payload)
|
||||
json_payload = json_bytes(
|
||||
json_payload = cached_json_bytes(
|
||||
{
|
||||
trigger: description
|
||||
for trigger, description in descriptions.items()
|
||||
|
||||
@@ -66,7 +66,11 @@ from .helpers.event import (
|
||||
async_call_later,
|
||||
)
|
||||
from .helpers.frame import ReportBehavior, report_usage
|
||||
from .helpers.json import json_bytes, json_bytes_sorted, json_fragment
|
||||
from .helpers.json import (
|
||||
cached_json_fragment,
|
||||
cached_json_fragment_sorted,
|
||||
json_fragment,
|
||||
)
|
||||
from .helpers.typing import (
|
||||
UNDEFINED,
|
||||
ConfigType,
|
||||
@@ -682,7 +686,7 @@ class ConfigEntry[_DataT = Any]:
|
||||
),
|
||||
"num_subentries": len(self.subentries),
|
||||
}
|
||||
return json_fragment(json_bytes(json_repr))
|
||||
return cached_json_fragment(json_repr)
|
||||
|
||||
def clear_storage_cache(self) -> None:
|
||||
"""Clear cached properties that are included in as_storage_fragment."""
|
||||
@@ -691,7 +695,7 @@ class ConfigEntry[_DataT = Any]:
|
||||
@cached_property
|
||||
def as_storage_fragment(self) -> json_fragment:
|
||||
"""Return a storage fragment for this entry."""
|
||||
return json_fragment(json_bytes_sorted(self.as_dict()))
|
||||
return cached_json_fragment_sorted(self.as_dict())
|
||||
|
||||
async def async_setup(
|
||||
self,
|
||||
|
||||
@@ -140,6 +140,16 @@ def cached_json_fragment(data: Any) -> orjson.Fragment:
|
||||
return orjson.Fragment(b"".join((json_bytes(data), b"")))
|
||||
|
||||
|
||||
def cached_json_fragment_sorted(data: Any) -> orjson.Fragment:
|
||||
"""Return a json fragment with sorted keys, right-sized for long-term caching.
|
||||
|
||||
The sorted-key variant of cached_json_fragment (see json_bytes_sorted).
|
||||
"""
|
||||
# The empty second join item is load-bearing: it forces a copy into a
|
||||
# right-sized buffer; a single-item join returns the input unchanged.
|
||||
return orjson.Fragment(b"".join((json_bytes_sorted(data), b"")))
|
||||
|
||||
|
||||
def json_dumps(data: Any) -> str:
|
||||
r"""Dump json string.
|
||||
|
||||
|
||||
@@ -45,7 +45,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 .helpers.json import cached_json_fragment, json_fragment
|
||||
from .helpers.typing import UNDEFINED, UndefinedType
|
||||
from .util.async_ import create_eager_task
|
||||
from .util.hass_dict import HassKey
|
||||
@@ -798,7 +798,7 @@ class Integration:
|
||||
@cached_property
|
||||
def manifest_json_fragment(self) -> json_fragment:
|
||||
"""Return manifest as a JSON fragment."""
|
||||
return json_fragment(json_bytes(self.manifest))
|
||||
return cached_json_fragment(self.manifest)
|
||||
|
||||
@cached_property
|
||||
def name(self) -> str:
|
||||
|
||||
@@ -21,6 +21,7 @@ from homeassistant.helpers.json import (
|
||||
JSONEncoder as DefaultHASSJSONEncoder,
|
||||
cached_json_bytes,
|
||||
cached_json_fragment,
|
||||
cached_json_fragment_sorted,
|
||||
find_paths_unserializable_data,
|
||||
json_bytes,
|
||||
json_bytes_sorted,
|
||||
@@ -236,10 +237,19 @@ def test_cached_json_bytes() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_cached_json_fragment_sorted() -> None:
|
||||
"""Test cached_json_fragment_sorted serializes with sorted keys."""
|
||||
data = {"c": 3, "a": 1, "b": 2}
|
||||
|
||||
fragment = cached_json_fragment_sorted(data)
|
||||
assert isinstance(fragment, json_fragment)
|
||||
assert json_dumps([fragment]) == '[{"a":1,"b":2,"c":3}]'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cached_serializer",
|
||||
[cached_json_bytes, cached_json_fragment],
|
||||
ids=["cached_json_bytes", "cached_json_fragment"],
|
||||
[cached_json_bytes, cached_json_fragment, cached_json_fragment_sorted],
|
||||
ids=["cached_json_bytes", "cached_json_fragment", "cached_json_fragment_sorted"],
|
||||
)
|
||||
def test_cached_json_helpers_trim_buffer(
|
||||
cached_serializer: Callable[[Any], object],
|
||||
|
||||
Reference in New Issue
Block a user