Skip to content

Commit

Permalink
Prevent overriding cached attribute as property (home-assistant#107657)
Browse files Browse the repository at this point in the history
* Prevent overriding cached attribute as property

* Remove debug
  • Loading branch information
emontnemery authored and tomaszsluszniak committed Jan 9, 2024
1 parent 04a5915 commit 00497ca
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
4 changes: 4 additions & 0 deletions homeassistant/helpers/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import math
import sys
from timeit import default_timer as timer
from types import FunctionType
from typing import (
TYPE_CHECKING,
Any,
Expand Down Expand Up @@ -381,6 +382,9 @@ def wrap_attr(cls: CachedProperties, property_name: str) -> None:
# Check if an _attr_ class attribute exits and move it to __attr_. We check
# __dict__ here because we don't care about _attr_ class attributes in parents.
if attr_name in cls.__dict__:
attr = getattr(cls, attr_name)
if isinstance(attr, (FunctionType, property)):
raise TypeError(f"Can't override {attr_name} in subclass")
setattr(cls, private_attr_name, getattr(cls, attr_name))
annotations = cls.__annotations__
if attr_name in annotations:
Expand Down
41 changes: 41 additions & 0 deletions tests/helpers/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2406,6 +2406,47 @@ def attribution(self) -> str | None:
assert getattr(ent[1], property) == values[0]


async def test_cached_entity_property_override(hass: HomeAssistant) -> None:
"""Test overriding cached _attr_ raises."""

class EntityWithClassAttribute1(entity.Entity):
"""A derived class which overrides an _attr_ from a parent."""

_attr_attribution: str

class EntityWithClassAttribute2(entity.Entity):
"""A derived class which overrides an _attr_ from a parent."""

_attr_attribution = "blabla"

class EntityWithClassAttribute3(entity.Entity):
"""A derived class which overrides an _attr_ from a parent."""

_attr_attribution: str = "blabla"

class EntityWithClassAttribute4(entity.Entity):
@property
def _attr_not_cached(self):
return "blabla"

class EntityWithClassAttribute5(entity.Entity):
def _attr_not_cached(self):
return "blabla"

with pytest.raises(TypeError):

class EntityWithClassAttribute6(entity.Entity):
@property
def _attr_attribution(self):
return "🤡"

with pytest.raises(TypeError):

class EntityWithClassAttribute7(entity.Entity):
def _attr_attribution(self):
return "🤡"


async def test_entity_report_deprecated_supported_features_values(
caplog: pytest.LogCaptureFixture,
) -> None:
Expand Down

0 comments on commit 00497ca

Please sign in to comment.