From 8d2daaa694074b7a676a368c788c316676914267 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 21 Jun 2023 22:23:35 +0200 Subject: [PATCH] Limit cache size of EntityValues (#94983) --- homeassistant/helpers/entity_values.py | 19 +++++++++++++------ tests/helpers/test_entity_values.py | 7 +++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/homeassistant/helpers/entity_values.py b/homeassistant/helpers/entity_values.py index d489a4b1d37..fe4a4249c54 100644 --- a/homeassistant/helpers/entity_values.py +++ b/homeassistant/helpers/entity_values.py @@ -3,14 +3,24 @@ from __future__ import annotations from collections import OrderedDict import fnmatch +from functools import lru_cache import re from typing import Any from homeassistant.core import split_entity_id +_MAX_EXPECTED_ENTITIES = 16384 + class EntityValues: - """Class to store entity id based values.""" + """Class to store entity id based values. + + This class is expected to only be used infrequently + as it caches all entity ids up to _MAX_EXPECTED_ENTITIES. + + The cache includes `self` so it is important to + only use this in places where usage of `EntityValues` is immortal. + """ def __init__( self, @@ -19,7 +29,6 @@ class EntityValues: glob: dict[str, dict[str, str]] | None = None, ) -> None: """Initialize an EntityConfigDict.""" - self._cache: dict[str, dict[str, str]] = {} self._exact = exact self._domain = domain @@ -32,13 +41,11 @@ class EntityValues: self._glob = compiled + @lru_cache(maxsize=_MAX_EXPECTED_ENTITIES) def get(self, entity_id: str) -> dict[str, str]: """Get config for an entity id.""" - if entity_id in self._cache: - return self._cache[entity_id] - domain, _ = split_entity_id(entity_id) - result = self._cache[entity_id] = {} + result: dict[str, str] = {} if self._domain is not None and domain in self._domain: result.update(self._domain[domain]) diff --git a/tests/helpers/test_entity_values.py b/tests/helpers/test_entity_values.py index f953ec0ba9e..1ac8e480f51 100644 --- a/tests/helpers/test_entity_values.py +++ b/tests/helpers/test_entity_values.py @@ -10,9 +10,12 @@ def test_override_single_value() -> None: """Test values with exact match.""" store = EV({ent: {"key": "value"}}) assert store.get(ent) == {"key": "value"} - assert len(store._cache) == 1 + assert store.get.cache_info().currsize == 1 + assert store.get.cache_info().misses == 1 assert store.get(ent) == {"key": "value"} - assert len(store._cache) == 1 + assert store.get.cache_info().currsize == 1 + assert store.get.cache_info().misses == 1 + assert store.get.cache_info().hits == 1 def test_override_by_domain() -> None: