From 837220cce40890e296920d33a623adbc11bd15a6 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 28 May 2021 11:01:28 +0200 Subject: [PATCH] Add deprecated backwards compatible history.LazyState (#51144) --- homeassistant/components/history/__init__.py | 20 +++--- homeassistant/helpers/deprecation.py | 72 +++++++++++++------- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/history/__init__.py b/homeassistant/components/history/__init__.py index c92718a87e4..ac8a13e69ad 100644 --- a/homeassistant/components/history/__init__.py +++ b/homeassistant/components/history/__init__.py @@ -13,8 +13,7 @@ import voluptuous as vol from homeassistant.components import websocket_api from homeassistant.components.http import HomeAssistantView -from homeassistant.components.recorder import history -from homeassistant.components.recorder.models import States +from homeassistant.components.recorder import history, models as history_models from homeassistant.components.recorder.statistics import statistics_during_period from homeassistant.components.recorder.util import session_scope from homeassistant.const import ( @@ -26,7 +25,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.deprecation import deprecated_function +from homeassistant.helpers.deprecation import deprecated_class, deprecated_function from homeassistant.helpers.entityfilter import ( CONF_ENTITY_GLOBS, INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, @@ -110,6 +109,11 @@ async def async_setup(hass, config): return True +@deprecated_class("homeassistant.components.recorder.models.LazyState") +class LazyState(history_models.LazyState): + """A lazy version of core State.""" + + @websocket_api.websocket_command( { vol.Required("type"): "history/statistics_during_period", @@ -345,17 +349,17 @@ class Filters: """Generate the entity filter query.""" includes = [] if self.included_domains: - includes.append(States.domain.in_(self.included_domains)) + includes.append(history_models.States.domain.in_(self.included_domains)) if self.included_entities: - includes.append(States.entity_id.in_(self.included_entities)) + includes.append(history_models.States.entity_id.in_(self.included_entities)) for glob in self.included_entity_globs: includes.append(_glob_to_like(glob)) excludes = [] if self.excluded_domains: - excludes.append(States.domain.in_(self.excluded_domains)) + excludes.append(history_models.States.domain.in_(self.excluded_domains)) if self.excluded_entities: - excludes.append(States.entity_id.in_(self.excluded_entities)) + excludes.append(history_models.States.entity_id.in_(self.excluded_entities)) for glob in self.excluded_entity_globs: excludes.append(_glob_to_like(glob)) @@ -373,7 +377,7 @@ class Filters: def _glob_to_like(glob_str): """Translate glob to sql.""" - return States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS)) + return history_models.States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS)) def _entities_may_have_state_changes_after( diff --git a/homeassistant/helpers/deprecation.py b/homeassistant/helpers/deprecation.py index 06f09327dc9..adf3d8a5d88 100644 --- a/homeassistant/helpers/deprecation.py +++ b/homeassistant/helpers/deprecation.py @@ -80,6 +80,23 @@ def get_deprecated( return config.get(new_name, default) +def deprecated_class(replacement: str) -> Any: + """Mark class as deprecated and provide a replacement class to be used instead.""" + + def deprecated_decorator(cls: Any) -> Any: + """Decorate class as deprecated.""" + + @functools.wraps(cls) + def deprecated_cls(*args: tuple, **kwargs: dict[str, Any]) -> Any: + """Wrap for the original class.""" + _print_deprecation_warning(cls, replacement, "class") + return cls(*args, **kwargs) + + return deprecated_cls + + return deprecated_decorator + + def deprecated_function(replacement: str) -> Callable[..., Callable]: """Mark function as deprecated and provide a replacement function to be used instead.""" @@ -89,32 +106,39 @@ def deprecated_function(replacement: str) -> Callable[..., Callable]: @functools.wraps(func) def deprecated_func(*args: tuple, **kwargs: dict[str, Any]) -> Any: """Wrap for the original function.""" - logger = logging.getLogger(func.__module__) - try: - _, integration, path = get_integration_frame() - if path == "custom_components/": - logger.warning( - "%s was called from %s, this is a deprecated function. Use %s instead, please report this to the maintainer of %s", - func.__name__, - integration, - replacement, - integration, - ) - else: - logger.warning( - "%s was called from %s, this is a deprecated function. Use %s instead", - func.__name__, - integration, - replacement, - ) - except MissingIntegrationFrame: - logger.warning( - "%s is a deprecated function. Use %s instead", - func.__name__, - replacement, - ) + _print_deprecation_warning(func, replacement, "function") return func(*args, **kwargs) return deprecated_func return deprecated_decorator + + +def _print_deprecation_warning(obj: Any, replacement: str, description: str) -> None: + logger = logging.getLogger(obj.__module__) + try: + _, integration, path = get_integration_frame() + if path == "custom_components/": + logger.warning( + "%s was called from %s, this is a deprecated %s. Use %s instead, please report this to the maintainer of %s", + obj.__name__, + integration, + description, + replacement, + integration, + ) + else: + logger.warning( + "%s was called from %s, this is a deprecated %s. Use %s instead", + obj.__name__, + integration, + description, + replacement, + ) + except MissingIntegrationFrame: + logger.warning( + "%s is a deprecated %s. Use %s instead", + obj.__name__, + description, + replacement, + )