Make core States use cached_property (#100312)

Need to validate this is worth removing __slots__
This commit is contained in:
J. Nick Koston 2023-09-13 19:33:25 -05:00 committed by GitHub
parent 3cc9410a62
commit 547f32818c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 24 additions and 44 deletions

View file

@ -202,11 +202,11 @@ class APIStatesView(HomeAssistantView):
user: User = request["hass_user"]
hass: HomeAssistant = request.app["hass"]
if user.is_admin:
states = (state.as_dict_json() for state in hass.states.async_all())
states = (state.as_dict_json for state in hass.states.async_all())
else:
entity_perm = user.permissions.check_entity
states = (
state.as_dict_json()
state.as_dict_json
for state in hass.states.async_all()
if entity_perm(state.entity_id, "read")
)
@ -233,7 +233,7 @@ class APIEntityStateView(HomeAssistantView):
if state := hass.states.get(entity_id):
return web.Response(
body=state.as_dict_json(),
body=state.as_dict_json,
content_type=CONTENT_TYPE_JSON,
)
return self.json_message("Entity not found.", HTTPStatus.NOT_FOUND)

View file

@ -270,7 +270,7 @@ def handle_get_states(
states = _async_get_allowed_states(hass, connection)
try:
serialized_states = [state.as_dict_json() for state in states]
serialized_states = [state.as_dict_json for state in states]
except (ValueError, TypeError):
pass
else:
@ -281,7 +281,7 @@ def handle_get_states(
serialized_states = []
for state in states:
try:
serialized_states.append(state.as_dict_json())
serialized_states.append(state.as_dict_json)
except (ValueError, TypeError):
connection.logger.error(
"Unable to serialize to JSON. Bad data found at %s",
@ -358,7 +358,7 @@ def handle_subscribe_entities(
# to succeed for the UI to show.
try:
serialized_states = [
state.as_compressed_state_json()
state.as_compressed_state_json
for state in states
if not entity_ids or state.entity_id in entity_ids
]
@ -371,7 +371,7 @@ def handle_subscribe_entities(
serialized_states = []
for state in states:
try:
serialized_states.append(state.as_compressed_state_json())
serialized_states.append(state.as_compressed_state_json)
except (ValueError, TypeError):
connection.logger.error(
"Unable to serialize to JSON. Bad data found at %s",

View file

@ -141,7 +141,7 @@ def _state_diff_event(event: Event) -> dict:
if (event_old_state := event.data["old_state"]) is None:
return {
ENTITY_EVENT_ADD: {
event_new_state.entity_id: event_new_state.as_compressed_state()
event_new_state.entity_id: event_new_state.as_compressed_state
}
}
if TYPE_CHECKING:

View file

@ -35,6 +35,7 @@ import voluptuous as vol
import yarl
from . import block_async_io, util
from .backports.functools import cached_property
from .const import (
ATTR_DOMAIN,
ATTR_FRIENDLY_NAME,
@ -1239,20 +1240,6 @@ class State:
object_id: Object id of this state.
"""
__slots__ = (
"entity_id",
"state",
"attributes",
"last_changed",
"last_updated",
"context",
"domain",
"object_id",
"_as_dict",
"_as_dict_json",
"_as_compressed_state_json",
)
def __init__(
self,
entity_id: str,
@ -1282,8 +1269,6 @@ class State:
self.context = context or Context()
self.domain, self.object_id = split_entity_id(self.entity_id)
self._as_dict: ReadOnlyDict[str, Collection[Any]] | None = None
self._as_dict_json: str | None = None
self._as_compressed_state_json: str | None = None
@property
def name(self) -> str:
@ -1318,12 +1303,12 @@ class State:
)
return self._as_dict
@cached_property
def as_dict_json(self) -> str:
"""Return a JSON string of the State."""
if not self._as_dict_json:
self._as_dict_json = json_dumps(self.as_dict())
return self._as_dict_json
return json_dumps(self.as_dict())
@cached_property
def as_compressed_state(self) -> dict[str, Any]:
"""Build a compressed dict of a state for adds.
@ -1348,6 +1333,7 @@ class State:
)
return compressed_state
@cached_property
def as_compressed_state_json(self) -> str:
"""Build a compressed JSON key value pair of a state for adds.
@ -1355,11 +1341,7 @@ class State:
It is used for sending multiple states in a single message.
"""
if not self._as_compressed_state_json:
self._as_compressed_state_json = json_dumps(
{self.entity_id: self.as_compressed_state()}
)[1:-1]
return self._as_compressed_state_json
return json_dumps({self.entity_id: self.as_compressed_state})[1:-1]
@classmethod
def from_dict(cls, json_dict: dict[str, Any]) -> Self | None:

View file

@ -929,8 +929,6 @@ class DomainStates:
class TemplateStateBase(State):
"""Class to represent a state object in a template."""
__slots__ = ("_hass", "_collect", "_entity_id", "__dict__")
_state: State
__setitem__ = _readonly

View file

@ -671,11 +671,11 @@ def test_state_as_dict_json() -> None:
'"last_changed":"1984-12-08T12:00:00","last_updated":"1984-12-08T12:00:00",'
'"context":{"id":"01H0D6K3RFJAYAV2093ZW30PCW","parent_id":null,"user_id":null}}'
)
as_dict_json_1 = state.as_dict_json()
as_dict_json_1 = state.as_dict_json
assert as_dict_json_1 == expected
# 2nd time to verify cache
assert state.as_dict_json() == expected
assert state.as_dict_json() is as_dict_json_1
assert state.as_dict_json == expected
assert state.as_dict_json is as_dict_json_1
def test_state_as_compressed_state() -> None:
@ -694,12 +694,12 @@ def test_state_as_compressed_state() -> None:
"lc": last_time.timestamp(),
"s": "on",
}
as_compressed_state = state.as_compressed_state()
as_compressed_state = state.as_compressed_state
# We are not too concerned about these being ReadOnlyDict
# since we don't expect them to be called by external callers
assert as_compressed_state == expected
# 2nd time to verify cache
assert state.as_compressed_state() == expected
assert state.as_compressed_state == expected
def test_state_as_compressed_state_unique_last_updated() -> None:
@ -720,12 +720,12 @@ def test_state_as_compressed_state_unique_last_updated() -> None:
"lu": last_updated.timestamp(),
"s": "on",
}
as_compressed_state = state.as_compressed_state()
as_compressed_state = state.as_compressed_state
# We are not too concerned about these being ReadOnlyDict
# since we don't expect them to be called by external callers
assert as_compressed_state == expected
# 2nd time to verify cache
assert state.as_compressed_state() == expected
assert state.as_compressed_state == expected
def test_state_as_compressed_state_json() -> None:
@ -740,13 +740,13 @@ def test_state_as_compressed_state_json() -> None:
context=ha.Context(id="01H0D6H5K3SZJ3XGDHED1TJ79N"),
)
expected = '"happy.happy":{"s":"on","a":{"pig":"dog"},"c":"01H0D6H5K3SZJ3XGDHED1TJ79N","lc":471355200.0}'
as_compressed_state = state.as_compressed_state_json()
as_compressed_state = state.as_compressed_state_json
# We are not too concerned about these being ReadOnlyDict
# since we don't expect them to be called by external callers
assert as_compressed_state == expected
# 2nd time to verify cache
assert state.as_compressed_state_json() == expected
assert state.as_compressed_state_json() is as_compressed_state
assert state.as_compressed_state_json == expected
assert state.as_compressed_state_json is as_compressed_state
async def test_eventbus_add_remove_listener(hass: HomeAssistant) -> None: