Compare commits

...

7 Commits

Author SHA1 Message Date
epenet
43973667cf Only call get_annotations if there are cached properties 2026-03-12 09:04:11 +00:00
epenet
74784e7812 Improve 2026-03-12 08:35:59 +00:00
epenet
8371c63be5 Read and Write annotations only once 2026-03-12 08:14:10 +00:00
epenet
d42ae0de8a Improve 2026-03-12 08:00:23 +00:00
epenet
32e1b6a7c8 Adjust knx base entity 2026-03-12 07:42:55 +00:00
epenet
b6ac711ef0 Also adjust frozen_data_class_compat 2026-03-11 14:25:13 +00:00
epenet
33817ea292 Remove from __future__ import annotations in entity helper 2026-03-11 13:58:02 +00:00
3 changed files with 24 additions and 11 deletions

View File

@@ -1,7 +1,5 @@
"""Base classes for KNX entities."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from xknx.devices import Device as XknxDevice

View File

@@ -1,8 +1,7 @@
"""An abstract class for entities."""
from __future__ import annotations
from abc import ABCMeta
from annotationlib import Format, get_annotations
import asyncio
from collections import deque
from collections.abc import Callable, Coroutine, Iterable, Mapping
@@ -310,6 +309,8 @@ class CachedProperties(type):
Wrap _attr_ for cached properties in property objects.
"""
cls_annotations: dict[str, Any] | None = None
def deleter(name: str) -> Callable[[Any], None]:
"""Create a deleter for an _attr_ property."""
private_attr_name = f"__attr_{name}"
@@ -371,9 +372,14 @@ class CachedProperties(type):
if isinstance(attr, (FunctionType, property)):
raise TypeError(f"Can't override {attr_name} in subclass")
setattr(cls, private_attr_name, attr)
annotations = cls.__annotations__
if attr_name in annotations:
annotations[private_attr_name] = annotations.pop(attr_name)
# Check annotations as well.
nonlocal cls_annotations
if cls_annotations is None:
cls_annotations = get_annotations(cls, format=Format.FORWARDREF)
if attr_name in cls_annotations:
cls_annotations[private_attr_name] = cls_annotations.pop(attr_name)
# Create the _attr_ property
setattr(cls, attr_name, make_property(property_name))
@@ -400,6 +406,17 @@ class CachedProperties(type):
wrap_attr(cls, property_name)
seen_props.add(property_name)
if cls_annotations:
# Update class annotations with the new (_attr_ => __attr_) annotations
if "__annotations__" in cls.__dict__:
cls.__annotations__ = cls_annotations
else:
def wrapped_annotate(format: Format) -> dict[str, Any]:
return cls_annotations
cls.__annotate__ = wrapped_annotate
class ABCCachedProperties(CachedProperties, ABCMeta):
"""Add ABCMeta to CachedProperties."""

View File

@@ -4,8 +4,6 @@ This module enabled a non-breaking transition from mutable to frozen dataclasses
derived from EntityDescription and sub classes thereof.
"""
from __future__ import annotations
from annotationlib import Format, get_annotations
import dataclasses
import sys
@@ -93,7 +91,7 @@ class FrozenOrThawed(type):
# All direct parents are dataclasses, rely on dataclass inheritance
return
# Parent is not a dataclass, inject all parents' annotations
annotations: dict = {}
annotations: dict[str, Any] = {}
for parent in cls.__mro__[::-1]:
if parent is object:
continue
@@ -103,7 +101,7 @@ class FrozenOrThawed(type):
cls.__annotations__ = annotations
else:
def wrapped_annotate(format: Format) -> dict:
def wrapped_annotate(format: Format) -> dict[str, Any]:
return annotations
cls.__annotate__ = wrapped_annotate