Restore __slots__ to registry entries (#127481)

This commit is contained in:
J. Nick Koston 2024-10-05 05:16:52 -05:00 committed by GitHub
parent c104e66964
commit 24fbc366a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 23 deletions

View File

@ -104,8 +104,12 @@ async def async_get_config_entry_diagnostics(
if state := hass.states.get(entity.entity_id): if state := hass.states.get(entity.entity_id):
state_dict = dict(state.as_dict()) state_dict = dict(state.as_dict())
state_dict.pop("context", None) state_dict.pop("context", None)
entities.append({"entity": asdict(entity), "state": state_dict}) entity_dict = asdict(entity)
device_entities.append({"device": asdict(device), "entities": entities}) entity_dict.pop("_cache", None)
entities.append({"entity": entity_dict, "state": state_dict})
device_dict = asdict(device)
device_dict.pop("_cache", None)
device_entities.append({"device": device_dict, "entities": entities})
# remove envoy serial # remove envoy serial
old_serial = coordinator.envoy_serial_number old_serial = coordinator.envoy_serial_number

View File

@ -7,9 +7,7 @@ from collections.abc import Iterable
import dataclasses import dataclasses
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime
from typing import Any, Literal, TypedDict from typing import TYPE_CHECKING, Any, Literal, TypedDict
from propcache import cached_property
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.util.dt import utc_from_timestamp, utcnow from homeassistant.util.dt import utc_from_timestamp, utcnow
@ -27,6 +25,13 @@ from .singleton import singleton
from .storage import Store from .storage import Store
from .typing import UNDEFINED, UndefinedType from .typing import UNDEFINED, UndefinedType
if TYPE_CHECKING:
# mypy cannot workout _cache Protocol with dataclasses
from propcache import cached_property as under_cached_property
else:
from propcache import under_cached_property
DATA_REGISTRY: HassKey[AreaRegistry] = HassKey("area_registry") DATA_REGISTRY: HassKey[AreaRegistry] = HassKey("area_registry")
EVENT_AREA_REGISTRY_UPDATED: EventType[EventAreaRegistryUpdatedData] = EventType( EVENT_AREA_REGISTRY_UPDATED: EventType[EventAreaRegistryUpdatedData] = EventType(
"area_registry_updated" "area_registry_updated"
@ -63,7 +68,7 @@ class EventAreaRegistryUpdatedData(TypedDict):
area_id: str area_id: str
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True, slots=True)
class AreaEntry(NormalizedNameBaseRegistryEntry): class AreaEntry(NormalizedNameBaseRegistryEntry):
"""Area Registry Entry.""" """Area Registry Entry."""
@ -73,8 +78,9 @@ class AreaEntry(NormalizedNameBaseRegistryEntry):
id: str id: str
labels: set[str] = field(default_factory=set) labels: set[str] = field(default_factory=set)
picture: str | None picture: str | None
_cache: dict[str, Any] = field(default_factory=dict, compare=False, init=False)
@cached_property @under_cached_property
def json_fragment(self) -> json_fragment: def json_fragment(self) -> json_fragment:
"""Return a JSON representation of this AreaEntry.""" """Return a JSON representation of this AreaEntry."""
return json_fragment( return json_fragment(

View File

@ -12,7 +12,6 @@ import time
from typing import TYPE_CHECKING, Any, Literal, TypedDict from typing import TYPE_CHECKING, Any, Literal, TypedDict
import attr import attr
from propcache import cached_property
from yarl import URL from yarl import URL
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
@ -46,9 +45,14 @@ from .singleton import singleton
from .typing import UNDEFINED, UndefinedType from .typing import UNDEFINED, UndefinedType
if TYPE_CHECKING: if TYPE_CHECKING:
# mypy cannot workout _cache Protocol with attrs
from propcache import cached_property as under_cached_property
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from . import entity_registry from . import entity_registry
else:
from propcache import under_cached_property
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -278,7 +282,7 @@ def _validate_configuration_url(value: Any) -> str | None:
return url_as_str return url_as_str
@attr.s(frozen=True) @attr.s(frozen=True, slots=True)
class DeviceEntry: class DeviceEntry:
"""Device Registry Entry.""" """Device Registry Entry."""
@ -306,6 +310,7 @@ class DeviceEntry:
via_device_id: str | None = attr.ib(default=None) via_device_id: str | None = attr.ib(default=None)
# This value is not stored, just used to keep track of events to fire. # This value is not stored, just used to keep track of events to fire.
is_new: bool = attr.ib(default=False) is_new: bool = attr.ib(default=False)
_cache: dict[str, Any] = attr.ib(factory=dict, eq=False, init=False)
@property @property
def disabled(self) -> bool: def disabled(self) -> bool:
@ -342,7 +347,7 @@ class DeviceEntry:
"via_device_id": self.via_device_id, "via_device_id": self.via_device_id,
} }
@cached_property @under_cached_property
def json_repr(self) -> bytes | None: def json_repr(self) -> bytes | None:
"""Return a cached JSON representation of the entry.""" """Return a cached JSON representation of the entry."""
try: try:
@ -358,7 +363,7 @@ class DeviceEntry:
) )
return None return None
@cached_property @under_cached_property
def as_storage_fragment(self) -> json_fragment: def as_storage_fragment(self) -> json_fragment:
"""Return a json fragment for storage.""" """Return a json fragment for storage."""
return json_fragment( return json_fragment(
@ -390,7 +395,7 @@ class DeviceEntry:
) )
@attr.s(frozen=True) @attr.s(frozen=True, slots=True)
class DeletedDeviceEntry: class DeletedDeviceEntry:
"""Deleted Device Registry Entry.""" """Deleted Device Registry Entry."""
@ -401,6 +406,7 @@ class DeletedDeviceEntry:
orphaned_timestamp: float | None = attr.ib() orphaned_timestamp: float | None = attr.ib()
created_at: datetime = attr.ib(factory=utcnow) created_at: datetime = attr.ib(factory=utcnow)
modified_at: datetime = attr.ib(factory=utcnow) modified_at: datetime = attr.ib(factory=utcnow)
_cache: dict[str, Any] = attr.ib(factory=dict, eq=False, init=False)
def to_device_entry( def to_device_entry(
self, self,
@ -419,7 +425,7 @@ class DeletedDeviceEntry:
is_new=True, is_new=True,
) )
@cached_property @under_cached_property
def as_storage_fragment(self) -> json_fragment: def as_storage_fragment(self) -> json_fragment:
"""Return a json fragment for storage.""" """Return a json fragment for storage."""
return json_fragment( return json_fragment(

View File

@ -19,7 +19,6 @@ import time
from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict
import attr import attr
from propcache import cached_property
import voluptuous as vol import voluptuous as vol
from homeassistant.const import ( from homeassistant.const import (
@ -65,7 +64,12 @@ from .singleton import singleton
from .typing import UNDEFINED, UndefinedType from .typing import UNDEFINED, UndefinedType
if TYPE_CHECKING: if TYPE_CHECKING:
# mypy cannot workout _cache Protocol with attrs
from propcache import cached_property as under_cached_property
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
else:
from propcache import under_cached_property
DATA_REGISTRY: HassKey[EntityRegistry] = HassKey("entity_registry") DATA_REGISTRY: HassKey[EntityRegistry] = HassKey("entity_registry")
EVENT_ENTITY_REGISTRY_UPDATED: EventType[EventEntityRegistryUpdatedData] = EventType( EVENT_ENTITY_REGISTRY_UPDATED: EventType[EventEntityRegistryUpdatedData] = EventType(
@ -162,7 +166,7 @@ def _protect_entity_options(
return ReadOnlyDict({key: ReadOnlyDict(val) for key, val in data.items()}) return ReadOnlyDict({key: ReadOnlyDict(val) for key, val in data.items()})
@attr.s(frozen=True) @attr.s(frozen=True, slots=True)
class RegistryEntry: class RegistryEntry:
"""Entity Registry Entry.""" """Entity Registry Entry."""
@ -201,6 +205,7 @@ class RegistryEntry:
supported_features: int = attr.ib(default=0) supported_features: int = attr.ib(default=0)
translation_key: str | None = attr.ib(default=None) translation_key: str | None = attr.ib(default=None)
unit_of_measurement: str | None = attr.ib(default=None) unit_of_measurement: str | None = attr.ib(default=None)
_cache: dict[str, Any] = attr.ib(factory=dict, eq=False, init=False)
@domain.default @domain.default
def _domain_default(self) -> str: def _domain_default(self) -> str:
@ -247,7 +252,7 @@ class RegistryEntry:
display_dict["dp"] = precision display_dict["dp"] = precision
return display_dict return display_dict
@cached_property @under_cached_property
def display_json_repr(self) -> bytes | None: def display_json_repr(self) -> bytes | None:
"""Return a cached partial JSON representation of the entry. """Return a cached partial JSON representation of the entry.
@ -267,7 +272,7 @@ class RegistryEntry:
return None return None
return json_repr return json_repr
@cached_property @under_cached_property
def as_partial_dict(self) -> dict[str, Any]: def as_partial_dict(self) -> dict[str, Any]:
"""Return a partial dict representation of the entry.""" """Return a partial dict representation of the entry."""
# Convert sets and tuples to lists # Convert sets and tuples to lists
@ -296,7 +301,7 @@ class RegistryEntry:
"unique_id": self.unique_id, "unique_id": self.unique_id,
} }
@cached_property @under_cached_property
def extended_dict(self) -> dict[str, Any]: def extended_dict(self) -> dict[str, Any]:
"""Return a extended dict representation of the entry.""" """Return a extended dict representation of the entry."""
# Convert sets and tuples to lists # Convert sets and tuples to lists
@ -311,7 +316,7 @@ class RegistryEntry:
"original_icon": self.original_icon, "original_icon": self.original_icon,
} }
@cached_property @under_cached_property
def partial_json_repr(self) -> bytes | None: def partial_json_repr(self) -> bytes | None:
"""Return a cached partial JSON representation of the entry.""" """Return a cached partial JSON representation of the entry."""
try: try:
@ -327,7 +332,7 @@ class RegistryEntry:
) )
return None return None
@cached_property @under_cached_property
def as_storage_fragment(self) -> json_fragment: def as_storage_fragment(self) -> json_fragment:
"""Return a json fragment for storage.""" """Return a json fragment for storage."""
return json_fragment( return json_fragment(
@ -394,7 +399,7 @@ class RegistryEntry:
hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs) hass.states.async_set(self.entity_id, STATE_UNAVAILABLE, attrs)
@attr.s(frozen=True) @attr.s(frozen=True, slots=True)
class DeletedRegistryEntry: class DeletedRegistryEntry:
"""Deleted Entity Registry Entry.""" """Deleted Entity Registry Entry."""
@ -407,13 +412,14 @@ class DeletedRegistryEntry:
orphaned_timestamp: float | None = attr.ib() orphaned_timestamp: float | None = attr.ib()
created_at: datetime = attr.ib(factory=utcnow) created_at: datetime = attr.ib(factory=utcnow)
modified_at: datetime = attr.ib(factory=utcnow) modified_at: datetime = attr.ib(factory=utcnow)
_cache: dict[str, Any] = attr.ib(factory=dict, eq=False, init=False)
@domain.default @domain.default
def _domain_default(self) -> str: def _domain_default(self) -> str:
"""Compute domain value.""" """Compute domain value."""
return split_entity_id(self.entity_id)[0] return split_entity_id(self.entity_id)[0]
@cached_property @under_cached_property
def as_storage_fragment(self) -> json_fragment: def as_storage_fragment(self) -> json_fragment:
"""Return a json fragment for storage.""" """Return a json fragment for storage."""
return json_fragment( return json_fragment(

View File

@ -27,7 +27,7 @@ from tests.common import MockConfigEntry, MockModule, mock_integration, mock_pla
from tests.typing import WebSocketGenerator from tests.typing import WebSocketGenerator
@attr.s(frozen=True) @attr.s(frozen=True, slots=True)
class MockDeviceEntry(dr.DeviceEntry): class MockDeviceEntry(dr.DeviceEntry):
"""Device Registry Entry with fixed UUID.""" """Device Registry Entry with fixed UUID."""

View File

@ -289,6 +289,7 @@ async def test_snapshots(
entry.pop("device_id", None) entry.pop("device_id", None)
entry.pop("created_at", None) entry.pop("created_at", None)
entry.pop("modified_at", None) entry.pop("modified_at", None)
entry.pop("_cache", None)
entities.append({"entry": entry, "state": state_dict}) entities.append({"entry": entry, "state": state_dict})
@ -297,6 +298,8 @@ async def test_snapshots(
device_dict.pop("via_device_id", None) device_dict.pop("via_device_id", None)
device_dict.pop("created_at", None) device_dict.pop("created_at", None)
device_dict.pop("modified_at", None) device_dict.pop("modified_at", None)
device_dict.pop("_cache", None)
devices.append({"device": device_dict, "entities": entities}) devices.append({"device": device_dict, "entities": entities})
assert snapshot == devices assert snapshot == devices

View File

@ -132,6 +132,7 @@ class HomeAssistantSnapshotSerializer(AmberDataSerializer):
"""Prepare a Home Assistant area registry entry for serialization.""" """Prepare a Home Assistant area registry entry for serialization."""
serialized = AreaRegistryEntrySnapshot(dataclasses.asdict(data) | {"id": ANY}) serialized = AreaRegistryEntrySnapshot(dataclasses.asdict(data) | {"id": ANY})
serialized.pop("_json_repr") serialized.pop("_json_repr")
serialized.pop("_cache")
return serialized return serialized
@classmethod @classmethod
@ -156,6 +157,7 @@ class HomeAssistantSnapshotSerializer(AmberDataSerializer):
serialized["via_device_id"] = ANY serialized["via_device_id"] = ANY
if serialized["primary_config_entry"] is not None: if serialized["primary_config_entry"] is not None:
serialized["primary_config_entry"] = ANY serialized["primary_config_entry"] = ANY
serialized.pop("_cache")
return cls._remove_created_and_modified_at(serialized) return cls._remove_created_and_modified_at(serialized)
@classmethod @classmethod
@ -182,6 +184,7 @@ class HomeAssistantSnapshotSerializer(AmberDataSerializer):
} }
) )
serialized.pop("categories") serialized.pop("categories")
serialized.pop("_cache")
return cls._remove_created_and_modified_at(serialized) return cls._remove_created_and_modified_at(serialized)
@classmethod @classmethod