Import and cache supported feature enum flags only when needed (#117270)

* Import and cache supported feature enum flags only when needed

* Add comment aboud being loaded from executor.

---------

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Allen Porter 2024-05-13 21:07:39 -07:00 committed by GitHub
parent 13414a0a32
commit b84829f70f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 16 additions and 53 deletions

View File

@ -3,8 +3,9 @@
from __future__ import annotations
from collections.abc import Callable, Mapping, Sequence
from enum import IntFlag, StrEnum
from enum import StrEnum
from functools import cache
import importlib
from typing import Any, Generic, Literal, Required, TypedDict, TypeVar, cast
from uuid import UUID
@ -82,63 +83,23 @@ class Selector(Generic[_T]):
@cache
def _entity_features() -> dict[str, type[IntFlag]]:
"""Return a cached lookup of entity feature enums."""
# pylint: disable=import-outside-toplevel
from homeassistant.components.alarm_control_panel import (
AlarmControlPanelEntityFeature,
)
from homeassistant.components.calendar import CalendarEntityFeature
from homeassistant.components.camera import CameraEntityFeature
from homeassistant.components.climate import ClimateEntityFeature
from homeassistant.components.cover import CoverEntityFeature
from homeassistant.components.fan import FanEntityFeature
from homeassistant.components.humidifier import HumidifierEntityFeature
from homeassistant.components.lawn_mower import LawnMowerEntityFeature
from homeassistant.components.light import LightEntityFeature
from homeassistant.components.lock import LockEntityFeature
from homeassistant.components.media_player import MediaPlayerEntityFeature
from homeassistant.components.notify import NotifyEntityFeature
from homeassistant.components.remote import RemoteEntityFeature
from homeassistant.components.siren import SirenEntityFeature
from homeassistant.components.todo import TodoListEntityFeature
from homeassistant.components.update import UpdateEntityFeature
from homeassistant.components.vacuum import VacuumEntityFeature
from homeassistant.components.valve import ValveEntityFeature
from homeassistant.components.water_heater import WaterHeaterEntityFeature
from homeassistant.components.weather import WeatherEntityFeature
def _entity_feature_flag(domain: str, enum_name: str, feature_name: str) -> int:
"""Return a cached lookup of an entity feature enum.
return {
"AlarmControlPanelEntityFeature": AlarmControlPanelEntityFeature,
"CalendarEntityFeature": CalendarEntityFeature,
"CameraEntityFeature": CameraEntityFeature,
"ClimateEntityFeature": ClimateEntityFeature,
"CoverEntityFeature": CoverEntityFeature,
"FanEntityFeature": FanEntityFeature,
"HumidifierEntityFeature": HumidifierEntityFeature,
"LawnMowerEntityFeature": LawnMowerEntityFeature,
"LightEntityFeature": LightEntityFeature,
"LockEntityFeature": LockEntityFeature,
"MediaPlayerEntityFeature": MediaPlayerEntityFeature,
"NotifyEntityFeature": NotifyEntityFeature,
"RemoteEntityFeature": RemoteEntityFeature,
"SirenEntityFeature": SirenEntityFeature,
"TodoListEntityFeature": TodoListEntityFeature,
"UpdateEntityFeature": UpdateEntityFeature,
"VacuumEntityFeature": VacuumEntityFeature,
"ValveEntityFeature": ValveEntityFeature,
"WaterHeaterEntityFeature": WaterHeaterEntityFeature,
"WeatherEntityFeature": WeatherEntityFeature,
}
This will import a module from disk and is run from an executor when
loading the services schema files.
"""
module = importlib.import_module(f"homeassistant.components.{domain}")
enum = getattr(module, enum_name)
feature = getattr(enum, feature_name)
return cast(int, feature.value)
def _validate_supported_feature(supported_feature: str) -> int:
"""Validate a supported feature and resolve an enum string to its value."""
known_entity_features = _entity_features()
try:
_, enum, feature = supported_feature.split(".", 2)
domain, enum, feature = supported_feature.split(".", 2)
except ValueError as exc:
raise vol.Invalid(
f"Invalid supported feature '{supported_feature}', expected "
@ -146,8 +107,8 @@ def _validate_supported_feature(supported_feature: str) -> int:
) from exc
try:
return cast(int, getattr(known_entity_features[enum], feature).value)
except (AttributeError, KeyError) as exc:
return _entity_feature_flag(domain, enum, feature)
except (ModuleNotFoundError, AttributeError) as exc:
raise vol.Invalid(f"Unknown supported feature '{supported_feature}'") from exc

View File

@ -282,6 +282,8 @@ def test_entity_selector_schema(schema, valid_selections, invalid_selections) ->
{"filter": [{"supported_features": ["blah"]}]},
# Unknown feature enum
{"filter": [{"supported_features": ["blah.FooEntityFeature.blah"]}]},
# Unknown feature enum
{"filter": [{"supported_features": ["light.FooEntityFeature.blah"]}]},
# Unknown feature enum member
{"filter": [{"supported_features": ["light.LightEntityFeature.blah"]}]},
],