Prevent overriding cached attribute as property (#107657)

* Prevent overriding cached attribute as property

* Remove debug
This commit is contained in:
Erik Montnemery 2024-01-09 19:16:45 +01:00 committed by Franck Nijhof
parent 26da7402a2
commit 5b84e50dc0
No known key found for this signature in database
GPG key ID: D62583BA8AB11CA3
2 changed files with 45 additions and 0 deletions

View file

@ -13,6 +13,7 @@ import logging
import math import math
import sys import sys
from timeit import default_timer as timer from timeit import default_timer as timer
from types import FunctionType
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
@ -374,6 +375,9 @@ class CachedProperties(type):
# Check if an _attr_ class attribute exits and move it to __attr_. We check # 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. # __dict__ here because we don't care about _attr_ class attributes in parents.
if attr_name in cls.__dict__: 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)) setattr(cls, private_attr_name, getattr(cls, attr_name))
annotations = cls.__annotations__ annotations = cls.__annotations__
if attr_name in annotations: if attr_name in annotations:

View file

@ -2039,6 +2039,47 @@ async def test_cached_entity_property_class_attribute(hass: HomeAssistant) -> No
assert getattr(ent[1], property) == values[0] 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( async def test_entity_report_deprecated_supported_features_values(
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None: