1 """Deprecation helpers for Home Assistant."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from contextlib
import suppress
7 from enum
import Enum, EnumType, _EnumDict
11 from typing
import Any, NamedTuple
14 def deprecated_substitute[_ObjectT: object](
16 ) -> Callable[[Callable[[_ObjectT], Any]], Callable[[_ObjectT], Any]]:
17 """Help migrate properties to new names.
19 When a property is added to replace an older property, this decorator can
20 be added to the new property, listing the old property as the substitute.
21 If the old property is defined, its value will be used instead, and a log
22 warning will be issued alerting the user of the impending change.
25 def decorator(func: Callable[[_ObjectT], Any]) -> Callable[[_ObjectT], Any]:
26 """Decorate function as deprecated."""
28 def func_wrapper(self: _ObjectT) -> Any:
29 """Wrap for the original function."""
30 if hasattr(self, substitute_name):
33 warnings = getattr(func,
"_deprecated_substitute_warnings", {})
34 module_name = self.__module__
35 if not warnings.get(module_name):
36 logger = logging.getLogger(module_name)
39 "'%s' is deprecated. Please rename '%s' to "
40 "'%s' in '%s' to ensure future support."
45 inspect.getfile(self.__class__),
47 warnings[module_name] =
True
48 setattr(func,
"_deprecated_substitute_warnings", warnings)
51 return getattr(self, substitute_name)
60 config: dict[str, Any], new_name: str, old_name: str, default: Any |
None =
None
62 """Allow an old config name to be deprecated with a replacement.
64 If the new config isn't found, but the old one is, the old value is used
65 and a warning is issued to the user.
67 if old_name
in config:
68 module = inspect.getmodule(inspect.stack(context=0)[1].frame)
69 if module
is not None:
70 module_name = module.__name__
75 module_name = __name__
77 logger = logging.getLogger(module_name)
80 "'%s' is deprecated. Please rename '%s' to '%s' in your "
87 return config.get(old_name)
88 return config.get(new_name, default)
91 def deprecated_class[**_P, _R](
92 replacement: str, *, breaks_in_ha_version: str |
None =
None
93 ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
94 """Mark class as deprecated and provide a replacement class to be used instead.
96 If the deprecated function was called from a custom integration, ask the user to
100 def deprecated_decorator(cls: Callable[_P, _R]) -> Callable[_P, _R]:
101 """Decorate class as deprecated."""
103 @functools.wraps(cls)
104 def deprecated_cls(*args: _P.args, **kwargs: _P.kwargs) -> _R:
105 """Wrap for the original class."""
107 cls, replacement,
"class",
"instantiated", breaks_in_ha_version
109 return cls(*args, **kwargs)
111 return deprecated_cls
113 return deprecated_decorator
116 def deprecated_function[**_P, _R](
117 replacement: str, *, breaks_in_ha_version: str |
None =
None
118 ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
119 """Mark function as deprecated and provide a replacement to be used instead.
121 If the deprecated function was called from a custom integration, ask the user to
125 def deprecated_decorator(func: Callable[_P, _R]) -> Callable[_P, _R]:
126 """Decorate function as deprecated."""
128 @functools.wraps(func)
129 def deprecated_func(*args: _P.args, **kwargs: _P.kwargs) -> _R:
130 """Wrap for the original function."""
132 func, replacement,
"function",
"called", breaks_in_ha_version
134 return func(*args, **kwargs)
136 return deprecated_func
138 return deprecated_decorator
146 breaks_in_ha_version: str |
None,
154 breaks_in_ha_version,
155 log_when_no_integration_is_found=
True,
165 breaks_in_ha_version: str |
None,
167 log_when_no_integration_is_found: bool,
171 with suppress(ImportError):
178 breaks_in_ha_version,
179 log_when_no_integration_is_found=log_when_no_integration_is_found,
189 breaks_in_ha_version: str |
None,
191 log_when_no_integration_is_found: bool,
197 from .frame
import MissingIntegrationFrame, get_integration_frame
199 logger = logging.getLogger(module_name)
200 if breaks_in_ha_version:
201 breaks_in = f
" which will be removed in HA Core {breaks_in_ha_version}"
206 except MissingIntegrationFrame:
207 if log_when_no_integration_is_found:
209 "%s is a deprecated %s%s. Use %s instead",
216 if integration_frame.custom_integration:
219 integration_domain=integration_frame.integration,
220 module=integration_frame.module,
224 "%s was %s from %s, this is a deprecated %s%s. Use %s instead,"
229 integration_frame.integration,
237 "%s was %s from %s, this is a deprecated %s%s. Use %s instead",
240 integration_frame.integration,
248 """Deprecated constant."""
252 breaks_in_ha_version: str |
None
256 """Deprecated constant."""
259 breaks_in_ha_version: str |
None
263 """Deprecated alias."""
267 breaks_in_ha_version: str |
None
271 """Deprecated alias with deferred evaluation of the value."""
275 value_fn: Callable[[], Any],
277 breaks_in_ha_version: str |
None,
284 @functools.cached_property
286 """Return the value."""
290 _PREFIX_DEPRECATED =
"_DEPRECATED_"
294 """Check if the not found name is a deprecated constant.
296 If it is, print a deprecation warning and return the value of the constant.
297 Otherwise raise AttributeError.
299 module_name = module_globals.get(
"__name__")
300 value = replacement =
None
301 description =
"constant"
302 if (deprecated_const := module_globals.get(_PREFIX_DEPRECATED + name))
is None:
303 raise AttributeError(f
"Module {module_name!r} has no attribute {name!r}")
304 if isinstance(deprecated_const, DeprecatedConstant):
305 value = deprecated_const.value
306 replacement = deprecated_const.replacement
307 breaks_in_ha_version = deprecated_const.breaks_in_ha_version
308 elif isinstance(deprecated_const, DeprecatedConstantEnum):
309 value = deprecated_const.enum.value
311 f
"{deprecated_const.enum.__class__.__name__}.{deprecated_const.enum.name}"
313 breaks_in_ha_version = deprecated_const.breaks_in_ha_version
314 elif isinstance(deprecated_const, (DeprecatedAlias, DeferredDeprecatedAlias)):
315 description =
"alias"
316 value = deprecated_const.value
317 replacement = deprecated_const.replacement
318 breaks_in_ha_version = deprecated_const.breaks_in_ha_version
320 if value
is None or replacement
is None:
322 f
"Value of {_PREFIX_DEPRECATED}{name} is an instance of "
323 f
"{type(deprecated_const)} but an instance of DeprecatedAlias, "
324 "DeferredDeprecatedAlias, DeprecatedConstant or DeprecatedConstantEnum "
328 logging.getLogger(module_name).debug(msg)
333 raise AttributeError(msg)
337 module_name
or __name__,
341 breaks_in_ha_version,
342 log_when_no_integration_is_found=
False,
348 """Return dir() with deprecated constants."""
349 return module_globals_keys + [
350 name.removeprefix(_PREFIX_DEPRECATED)
351 for name
in module_globals_keys
352 if name.startswith(_PREFIX_DEPRECATED)
357 """Generate a list for __all___ with deprecated constants."""
360 module_globals_keys =
list(module_globals)
361 return [itm
for itm
in module_globals_keys
if not itm.startswith(
"_")] + [
362 name.removeprefix(_PREFIX_DEPRECATED)
363 for name
in module_globals_keys
364 if name.startswith(_PREFIX_DEPRECATED)
369 """Enum with deprecated members."""
374 bases: tuple[type, ...],
375 classdict: _EnumDict,
377 deprecated: dict[str, tuple[str, str]],
380 """Create a new class."""
381 classdict[
"__deprecated__"] = deprecated
382 return super().
__new__(mcs, cls, bases, classdict, **kwds)
385 """Warn if accessing a deprecated member."""
387 if name
in deprecated:
389 f
"{cls.__name__}.{name}",
391 f
"{deprecated[name][0]}",
395 log_when_no_integration_is_found=
False,
None __init__(self, Callable[[], Any] value_fn, str replacement, str|None breaks_in_ha_version)
Any __getattribute__(cls, str name)
Any __new__(mcs, str cls, tuple[type,...] bases, _EnumDict classdict, *dict[str, tuple[str, str]] deprecated, **Any kwds)
HomeAssistant|None async_get_hass_or_none()
None _print_deprecation_warning(Any obj, str replacement, str description, str verb, str|None breaks_in_ha_version)
None _print_deprecation_warning_internal(str obj_name, str module_name, str replacement, str description, str verb, str|None breaks_in_ha_version, *bool log_when_no_integration_is_found)
Any|None get_deprecated(dict[str, Any] config, str new_name, str old_name, Any|None default=None)
list[str] dir_with_deprecated_constants(list[str] module_globals_keys)
list[str] all_with_deprecated_constants(dict[str, Any] module_globals)
Any check_if_deprecated_constant(str name, dict[str, Any] module_globals)
None _print_deprecation_warning_internal_impl(str obj_name, str module_name, str replacement, str description, str verb, str|None breaks_in_ha_version, *bool log_when_no_integration_is_found)
IntegrationFrame get_integration_frame(set|None exclude_integrations=None)
str async_suggest_report_issue(HomeAssistant|None hass, *Integration|None integration=None, str|None integration_domain=None, str|None module=None)