Skip to content

Commit

Permalink
Revert "Cache entity properties that are never expected to change in …
Browse files Browse the repository at this point in the history
…the base class (#95315)"

This reverts commit 042776e.
  • Loading branch information
emontnemery authored Sep 15, 2023
1 parent 9470c71 commit 341b8aa
Show file tree
Hide file tree
Showing 35 changed files with 48 additions and 146 deletions.
12 changes: 6 additions & 6 deletions homeassistant/backports/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
from types import GenericAlias
from typing import Any, Generic, Self, TypeVar, overload

_T_co = TypeVar("_T_co", covariant=True)
_T = TypeVar("_T")


class cached_property(Generic[_T_co]): # pylint: disable=invalid-name
class cached_property(Generic[_T]):
"""Backport of Python 3.12's cached_property.
Includes https://github.com/python/cpython/pull/101890/files
"""

def __init__(self, func: Callable[[Any], _T_co]) -> None:
def __init__(self, func: Callable[[Any], _T]) -> None:
"""Initialize."""
self.func: Callable[[Any], _T_co] = func
self.func: Callable[[Any], _T] = func
self.attrname: str | None = None
self.__doc__ = func.__doc__

Expand All @@ -35,12 +35,12 @@ def __get__(self, instance: None, owner: type[Any] | None = None) -> Self:
...

@overload
def __get__(self, instance: Any, owner: type[Any] | None = None) -> _T_co:
def __get__(self, instance: Any, owner: type[Any] | None = None) -> _T:
...

def __get__(
self, instance: Any | None, owner: type[Any] | None = None
) -> _T_co | Self:
) -> _T | Self:
"""Get."""
if instance is None:
return self
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/abode/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ def is_on(self) -> bool:
"""Return True if the binary sensor is on."""
return cast(bool, self._device.is_on)

@property # type: ignore[override]
# We don't know if the class may be set late here
# so we need to override the property to disable the cache.
@property
def device_class(self) -> BinarySensorDeviceClass | None:
"""Return the class of the binary sensor."""
if self._device.get_value("is_window") == "1":
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/binary_sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -198,7 +197,7 @@ def _default_to_device_class_name(self) -> bool:
"""
return self.device_class is not None

@cached_property
@property
def device_class(self) -> BinarySensorDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/button/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_validation import ( # noqa: F401
Expand Down Expand Up @@ -97,7 +96,7 @@ def _default_to_device_class_name(self) -> bool:
"""
return self.device_class is not None

@cached_property
@property
def device_class(self) -> ButtonDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/cover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
SERVICE_CLOSE_COVER,
Expand Down Expand Up @@ -251,7 +250,7 @@ def current_cover_tilt_position(self) -> int | None:
"""
return self._attr_current_cover_tilt_position

@cached_property
@property
def device_class(self) -> CoverDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/date/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_DATE
from homeassistant.core import HomeAssistant, ServiceCall
Expand Down Expand Up @@ -76,7 +75,7 @@ class DateEntity(Entity):
_attr_native_value: date | None
_attr_state: None = None

@cached_property
@property
@final
def device_class(self) -> None:
"""Return the device class for the entity."""
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/datetime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
Expand Down Expand Up @@ -87,7 +86,7 @@ class DateTimeEntity(Entity):
_attr_state: None = None
_attr_native_value: datetime | None

@cached_property
@property
@final
def device_class(self) -> None:
"""Return entity device class."""
Expand Down
5 changes: 1 addition & 4 deletions homeassistant/components/dsmr/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,7 @@ def available(self) -> bool:
"""Entity is only available if there is a telegram."""
return self.telegram is not None

@property # type: ignore[override]
# The device class can change at runtime from GAS to ENERGY
# when new data is received. This should be remembered and restored
# at startup, but the integration currently doesn't support that.
@property
def device_class(self) -> SensorDeviceClass | None:
"""Return the device class of this entity."""
device_class = super().device_class
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/event/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import logging
from typing import Any, Self, final

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_validation import ( # noqa: F401
Expand Down Expand Up @@ -115,7 +114,7 @@ class EventEntity(RestoreEntity):
__last_event_type: str | None = None
__last_event_attributes: dict[str, Any] | None = None

@cached_property
@property
def device_class(self) -> EventDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
11 changes: 2 additions & 9 deletions homeassistant/components/filter/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,17 +220,10 @@ def __init__(
self._state: StateType = None
self._filters = filters
self._attr_icon = None
self._device_class = None
self._attr_device_class = None
self._attr_state_class = None
self._attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_id}

@property
# This property is not cached because the underlying source may
# not always be available.
def device_class(self) -> SensorDeviceClass | None: # type: ignore[override]
"""Return the device class of the sensor."""
return self._device_class

@callback
def _update_filter_sensor_state_event(
self, event: EventType[EventStateChangedData]
Expand Down Expand Up @@ -290,7 +283,7 @@ def _update_filter_sensor_state(
self._state = temp_state.state

self._attr_icon = new_state.attributes.get(ATTR_ICON, ICON)
self._device_class = new_state.attributes.get(ATTR_DEVICE_CLASS)
self._attr_device_class = new_state.attributes.get(ATTR_DEVICE_CLASS)
self._attr_state_class = new_state.attributes.get(ATTR_STATE_CLASS)

if self._attr_native_unit_of_measurement != new_state.attributes.get(
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/group/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.components.binary_sensor import (
DEVICE_CLASSES_SCHEMA,
DOMAIN as BINARY_SENSOR_DOMAIN,
Expand Down Expand Up @@ -148,7 +147,7 @@ def async_update_group_state(self) -> None:
# Set as ON if any / all member is ON
self._attr_is_on = self.mode(state == STATE_ON for state in states)

@cached_property
@property
def device_class(self) -> BinarySensorDeviceClass | None:
"""Return the sensor class of the binary sensor."""
return self._device_class
5 changes: 1 addition & 4 deletions homeassistant/components/group/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,7 @@ def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the sensor."""
return {ATTR_ENTITY_ID: self._entity_ids, **self._extra_state_attribute}

@property # type: ignore[override]
# Because the device class is calculated, there is no guarantee that the
# sensors will be available when the entity is created so we do not want to
# cache the value.
@property
def device_class(self) -> SensorDeviceClass | None:
"""Return device class."""
if self._attr_device_class is not None:
Expand Down
5 changes: 1 addition & 4 deletions homeassistant/components/here_travel_time/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,7 @@ def _handle_coordinator_update(self) -> None:
)
self.async_write_ha_state()

@property # type: ignore[override]
# This property is not cached because the attribute can change
# at run time. This is not expected, but it is currently how
# the HERE integration works.
@property
def attribution(self) -> str | None:
"""Return the attribution."""
if self.coordinator.data is not None:
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/huawei_lte/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,9 +760,7 @@ def icon(self) -> str | None:
return self.entity_description.icon_fn(self.state)
return self.entity_description.icon

@property # type: ignore[override]
# The device class might change at run time of the signal
# is not a number, so we override here.
@property
def device_class(self) -> SensorDeviceClass | None:
"""Return device class for sensor."""
if self.entity_description.device_class_fn:
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/humidifier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_MODE,
Expand Down Expand Up @@ -159,7 +158,7 @@ def capability_attributes(self) -> dict[str, Any]:

return data

@cached_property
@property
def device_class(self) -> HumidifierDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/image_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.components.camera import Image
from homeassistant.const import (
ATTR_ENTITY_ID,
Expand Down Expand Up @@ -157,7 +156,7 @@ def confidence(self) -> float | None:
return self.entity_description.confidence
return None

@cached_property
@property
def device_class(self) -> ImageProcessingDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
12 changes: 2 additions & 10 deletions homeassistant/components/integration/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,6 @@ def __init__(
self._source_entity: str = source_entity
self._last_valid_state: Decimal | None = None
self._attr_device_info = device_info
self._device_class: SensorDeviceClass | None = None

@property # type: ignore[override]
# The underlying source data may be unavailable at startup, so the device
# class may be set late so we need to override the property to disable the cache.
def device_class(self) -> SensorDeviceClass | None:
"""Return the device class of the sensor."""
return self._device_class

def _unit(self, source_unit: str) -> str:
"""Derive unit from the source sensor, SI prefix and time unit."""
Expand Down Expand Up @@ -296,7 +288,7 @@ async def async_added_to_hass(self) -> None:
err,
)

self._device_class = state.attributes.get(ATTR_DEVICE_CLASS)
self._attr_device_class = state.attributes.get(ATTR_DEVICE_CLASS)
self._unit_of_measurement = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)

@callback
Expand Down Expand Up @@ -327,7 +319,7 @@ def calc_integration(event: EventType[EventStateChangedData]) -> None:
and new_state.attributes.get(ATTR_DEVICE_CLASS)
== SensorDeviceClass.POWER
):
self._device_class = SensorDeviceClass.ENERGY
self._attr_device_class = SensorDeviceClass.ENERGY
self._attr_icon = None

self.async_write_ha_state()
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import voluptuous as vol
from yarl import URL

from homeassistant.backports.functools import cached_property
from homeassistant.components import websocket_api
from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView
from homeassistant.components.websocket_api import ERR_NOT_SUPPORTED, ERR_UNKNOWN_ERROR
Expand Down Expand Up @@ -496,7 +495,7 @@ class MediaPlayerEntity(Entity):
_attr_volume_level: float | None = None

# Implement these for your media player
@cached_property
@property
def device_class(self) -> MediaPlayerDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mobile_app/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def handle_sensor_registration(data):
)


class MobileAppBinarySensor(MobileAppEntity, BinarySensorEntity): # type: ignore[misc]
class MobileAppBinarySensor(MobileAppEntity, BinarySensorEntity):
"""Representation of an mobile app binary sensor."""

@property
Expand Down
4 changes: 1 addition & 3 deletions homeassistant/components/mobile_app/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ def entity_registry_enabled_default(self) -> bool:
"""Return if entity should be enabled by default."""
return not self._config.get(ATTR_SENSOR_DISABLED)

@property # type: ignore[override,unused-ignore]
# Because the device class is received later from the mobile app
# we do not want to cache the property
@property
def device_class(self):
"""Return the device class."""
return self._config.get(ATTR_SENSOR_DEVICE_CLASS)
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mobile_app/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def handle_sensor_registration(data):
)


class MobileAppSensor(MobileAppEntity, RestoreSensor): # type: ignore[misc]
class MobileAppSensor(MobileAppEntity, RestoreSensor):
"""Representation of an mobile app sensor."""

async def async_restore_last_state(self, last_state):
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/number/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import voluptuous as vol

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_MODE, CONF_UNIT_OF_MEASUREMENT, UnitOfTemperature
from homeassistant.core import HomeAssistant, ServiceCall, callback
Expand Down Expand Up @@ -232,7 +231,7 @@ def _default_to_device_class_name(self) -> bool:
"""
return self.device_class is not None

@cached_property
@property
def device_class(self) -> NumberDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
3 changes: 1 addition & 2 deletions homeassistant/components/sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from math import ceil, floor, isfinite, log10
from typing import Any, Final, Self, cast, final

from homeassistant.backports.functools import cached_property
from homeassistant.config_entries import ConfigEntry

# pylint: disable-next=hass-deprecated-import
Expand Down Expand Up @@ -260,7 +259,7 @@ def _default_to_device_class_name(self) -> bool:
"""
return self.device_class not in (None, SensorDeviceClass.ENUM)

@cached_property
@property
def device_class(self) -> SensorDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
Expand Down
Loading

0 comments on commit 341b8aa

Please sign in to comment.