Deprecated old backports and typing aliases (#114883)

This commit is contained in:
Marc Mueller 2024-04-07 01:15:30 +02:00 committed by GitHub
parent 8e645c9b32
commit 8324fd5d1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 250 additions and 30 deletions

View File

@ -9,8 +9,21 @@ import it.
from __future__ import annotations
from enum import StrEnum
from enum import StrEnum as _StrEnum
from functools import partial
__all__ = [
"StrEnum",
]
from homeassistant.helpers.deprecation import (
DeprecatedAlias,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
# StrEnum deprecated as of 2024.5 use enum.StrEnum instead.
_DEPRECATED_StrEnum = DeprecatedAlias(_StrEnum, "enum.StrEnum", "2025.5")
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())

View File

@ -9,8 +9,22 @@ import it.
from __future__ import annotations
from functools import cached_property
from functools import cached_property as _cached_property, partial
__all__ = [
"cached_property",
]
from homeassistant.helpers.deprecation import (
DeprecatedAlias,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
# cached_property deprecated as of 2024.5 use functools.cached_property instead.
_DEPRECATED_cached_property = DeprecatedAlias(
_cached_property, "functools.cached_property", "2025.5"
)
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())

View File

@ -7,12 +7,12 @@ import dataclasses
from dataclasses import dataclass, field
from typing import Literal, TypedDict, cast
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.util.ulid import ulid_now
from .registry import BaseRegistry
from .storage import Store
from .typing import UNDEFINED, EventType, UndefinedType
from .typing import UNDEFINED, UndefinedType
DATA_REGISTRY = "category_registry"
EVENT_CATEGORY_REGISTRY_UPDATED = "category_registry_updated"
@ -28,7 +28,7 @@ class EventCategoryRegistryUpdatedData(TypedDict):
category_id: str
EventCategoryRegistryUpdated = EventType[EventCategoryRegistryUpdatedData]
EventCategoryRegistryUpdated = Event[EventCategoryRegistryUpdatedData]
@dataclass(slots=True, kw_only=True, frozen=True)

View File

@ -243,6 +243,14 @@ class DeprecatedConstantEnum(NamedTuple):
breaks_in_ha_version: str | None
class DeprecatedAlias(NamedTuple):
"""Deprecated alias."""
value: Any
replacement: str
breaks_in_ha_version: str | None
_PREFIX_DEPRECATED = "_DEPRECATED_"
@ -254,6 +262,7 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
"""
module_name = module_globals.get("__name__")
value = replacement = None
description = "constant"
if (deprecated_const := module_globals.get(_PREFIX_DEPRECATED + name)) is None:
raise AttributeError(f"Module {module_name!r} has no attribute {name!r}")
if isinstance(deprecated_const, DeprecatedConstant):
@ -266,6 +275,11 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
f"{deprecated_const.enum.__class__.__name__}.{deprecated_const.enum.name}"
)
breaks_in_ha_version = deprecated_const.breaks_in_ha_version
elif isinstance(deprecated_const, DeprecatedAlias):
description = "alias"
value = deprecated_const.value
replacement = deprecated_const.replacement
breaks_in_ha_version = deprecated_const.breaks_in_ha_version
if value is None or replacement is None:
msg = (
@ -284,7 +298,7 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
name,
module_name or __name__,
replacement,
"constant",
description,
"used",
breaks_in_ha_version,
log_when_no_integration_is_found=False,

View File

@ -2,15 +2,22 @@
from collections.abc import Mapping
from enum import Enum
from functools import partial
from typing import Any, TypeVar
import homeassistant.core
from .deprecation import (
DeprecatedAlias,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
_DataT = TypeVar("_DataT")
GPSType = tuple[float, float]
ConfigType = dict[str, Any]
ContextType = homeassistant.core.Context
DiscoveryInfoType = dict[str, Any]
ServiceDataType = dict[str, Any]
StateType = str | int | float | None
@ -33,7 +40,23 @@ UNDEFINED = UndefinedType._singleton # pylint: disable=protected-access
# are not present in the core code base.
# They are kept in order not to break custom integrations
# that may rely on them.
# In due time they will be removed.
EventType = homeassistant.core.Event
HomeAssistantType = homeassistant.core.HomeAssistant
ServiceCallType = homeassistant.core.ServiceCall
# Deprecated as of 2024.5 use types from homeassistant.core instead.
_DEPRECATED_ContextType = DeprecatedAlias(
homeassistant.core.Context, "homeassistant.core.Context", "2025.5"
)
_DEPRECATED_EventType = DeprecatedAlias(
homeassistant.core.Event, "homeassistant.core.Event", "2025.5"
)
_DEPRECATED_HomeAssistantType = DeprecatedAlias(
homeassistant.core.HomeAssistant, "homeassistant.core.HomeAssistant", "2025.5"
)
_DEPRECATED_ServiceCallType = DeprecatedAlias(
homeassistant.core.ServiceCall, "homeassistant.core.ServiceCall", "2025.5"
)
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())

View File

@ -1642,6 +1642,40 @@ def import_and_test_deprecated_constant(
assert constant_name in module.__all__
def import_and_test_deprecated_alias(
caplog: pytest.LogCaptureFixture,
module: ModuleType,
alias_name: str,
replacement: Any,
breaks_in_ha_version: str,
) -> None:
"""Import and test deprecated alias replaced by a value.
- Import deprecated alias
- Assert value is the same as the replacement
- Assert a warning is logged
- Assert the deprecated alias is included in the modules.__dir__()
- Assert the deprecated alias is included in the modules.__all__()
"""
replacement_name = f"{replacement.__module__}.{replacement.__name__}"
value = import_deprecated_constant(module, alias_name)
assert value == replacement
assert (
module.__name__,
logging.WARNING,
(
f"{alias_name} was used from test_constant_deprecation,"
f" this is a deprecated alias which will be removed in HA Core {breaks_in_ha_version}. "
f"Use {replacement_name} instead, please report "
"it to the author of the 'test_constant_deprecation' custom integration"
),
) in caplog.record_tuples
# verify deprecated alias is included in dir()
assert alias_name in dir(module)
assert alias_name in module.__all__
def help_test_all(module: ModuleType) -> None:
"""Test module.__all__ is correctly set."""
assert set(module.__all__) == {

View File

@ -10,6 +10,7 @@ import pytest
from homeassistant.core import HomeAssistant
from homeassistant.helpers.deprecation import (
DeprecatedAlias,
DeprecatedConstant,
DeprecatedConstantEnum,
check_if_deprecated_constant,
@ -283,38 +284,59 @@ class TestDeprecatedConstantEnum(StrEnum):
TEST = "value"
def _get_value(obj: DeprecatedConstant | DeprecatedConstantEnum | tuple) -> Any:
if isinstance(obj, tuple):
if len(obj) == 2:
return obj[0].value
return obj[0]
def _get_value(
obj: DeprecatedConstant
| DeprecatedConstantEnum
| DeprecatedAlias
| tuple[Any, ...],
) -> Any:
if isinstance(obj, DeprecatedConstant):
return obj.value
if isinstance(obj, DeprecatedConstantEnum):
return obj.enum.value
if isinstance(obj, DeprecatedAlias):
return obj.value
if len(obj) == 2:
return obj[0].value
return obj[0]
@pytest.mark.parametrize(
("deprecated_constant", "extra_msg"),
("deprecated_constant", "extra_msg", "description"),
[
(
DeprecatedConstant("value", "NEW_CONSTANT", None),
". Use NEW_CONSTANT instead",
"constant",
),
(
DeprecatedConstant(1, "NEW_CONSTANT", "2099.1"),
" which will be removed in HA Core 2099.1. Use NEW_CONSTANT instead",
"constant",
),
(
DeprecatedConstantEnum(TestDeprecatedConstantEnum.TEST, None),
". Use TestDeprecatedConstantEnum.TEST instead",
"constant",
),
(
DeprecatedConstantEnum(TestDeprecatedConstantEnum.TEST, "2099.1"),
" which will be removed in HA Core 2099.1. Use TestDeprecatedConstantEnum.TEST instead",
"constant",
),
(
DeprecatedAlias(1, "new_alias", None),
". Use new_alias instead",
"alias",
),
(
DeprecatedAlias(1, "new_alias", "2099.1"),
" which will be removed in HA Core 2099.1. Use new_alias instead",
"alias",
),
],
)
@ -330,10 +352,14 @@ def _get_value(obj: DeprecatedConstant | DeprecatedConstantEnum | tuple) -> Any:
)
def test_check_if_deprecated_constant(
caplog: pytest.LogCaptureFixture,
deprecated_constant: DeprecatedConstant | DeprecatedConstantEnum | tuple,
deprecated_constant: DeprecatedConstant
| DeprecatedConstantEnum
| DeprecatedAlias
| tuple,
extra_msg: str,
module_name: str,
extra_extra_msg: str,
description: str,
) -> None:
"""Test check_if_deprecated_constant."""
module_globals = {
@ -378,28 +404,42 @@ def test_check_if_deprecated_constant(
assert (
module_name,
logging.WARNING,
f"TEST_CONSTANT was used from hue, this is a deprecated constant{extra_msg}{extra_extra_msg}",
f"TEST_CONSTANT was used from hue, this is a deprecated {description}{extra_msg}{extra_extra_msg}",
) in caplog.record_tuples
@pytest.mark.parametrize(
("deprecated_constant", "extra_msg"),
("deprecated_constant", "extra_msg", "description"),
[
(
DeprecatedConstant("value", "NEW_CONSTANT", None),
". Use NEW_CONSTANT instead",
"constant",
),
(
DeprecatedConstant(1, "NEW_CONSTANT", "2099.1"),
" which will be removed in HA Core 2099.1. Use NEW_CONSTANT instead",
"constant",
),
(
DeprecatedConstantEnum(TestDeprecatedConstantEnum.TEST, None),
". Use TestDeprecatedConstantEnum.TEST instead",
"constant",
),
(
DeprecatedConstantEnum(TestDeprecatedConstantEnum.TEST, "2099.1"),
" which will be removed in HA Core 2099.1. Use TestDeprecatedConstantEnum.TEST instead",
"constant",
),
(
DeprecatedAlias(1, "new_alias", None),
". Use new_alias instead",
"alias",
),
(
DeprecatedAlias(1, "new_alias", "2099.1"),
" which will be removed in HA Core 2099.1. Use new_alias instead",
"alias",
),
],
)
@ -412,9 +452,13 @@ def test_check_if_deprecated_constant(
)
def test_check_if_deprecated_constant_integration_not_found(
caplog: pytest.LogCaptureFixture,
deprecated_constant: DeprecatedConstant | DeprecatedConstantEnum | tuple,
deprecated_constant: DeprecatedConstant
| DeprecatedConstantEnum
| DeprecatedAlias
| tuple,
extra_msg: str,
module_name: str,
description: str,
) -> None:
"""Test check_if_deprecated_constant."""
module_globals = {
@ -432,7 +476,7 @@ def test_check_if_deprecated_constant_integration_not_found(
assert (
module_name,
logging.WARNING,
f"TEST_CONSTANT is a deprecated constant{extra_msg}",
f"TEST_CONSTANT is a deprecated {description}{extra_msg}",
) not in caplog.record_tuples

View File

@ -0,0 +1,37 @@
"""Test typing helper module."""
from __future__ import annotations
from typing import Any
import pytest
from homeassistant.core import Context, Event, HomeAssistant, ServiceCall
from homeassistant.helpers import typing as ha_typing
from tests.common import import_and_test_deprecated_alias
@pytest.mark.parametrize(
("alias_name", "replacement", "breaks_in_ha_version"),
[
("ContextType", Context, "2025.5"),
("EventType", Event, "2025.5"),
("HomeAssistantType", HomeAssistant, "2025.5"),
("ServiceCallType", ServiceCall, "2025.5"),
],
)
def test_deprecated_aliases(
caplog: pytest.LogCaptureFixture,
alias_name: str,
replacement: Any,
breaks_in_ha_version: str,
) -> None:
"""Test deprecated aliases."""
import_and_test_deprecated_alias(
caplog,
ha_typing,
alias_name,
replacement,
breaks_in_ha_version,
)

41
tests/test_backports.py Normal file
View File

@ -0,0 +1,41 @@
"""Test backports package."""
from __future__ import annotations
from enum import StrEnum
from functools import cached_property
from types import ModuleType
from typing import Any
import pytest
from homeassistant.backports import (
enum as backports_enum,
functools as backports_functools,
)
from tests.common import import_and_test_deprecated_alias
@pytest.mark.parametrize(
("module", "replacement", "breaks_in_ha_version"),
[
(backports_enum, StrEnum, "2025.5"),
(backports_functools, cached_property, "2025.5"),
],
)
def test_deprecated_aliases(
caplog: pytest.LogCaptureFixture,
module: ModuleType,
replacement: Any,
breaks_in_ha_version: str,
) -> None:
"""Test deprecated aliases."""
alias_name = replacement.__name__
import_and_test_deprecated_alias(
caplog,
module,
alias_name,
replacement,
breaks_in_ha_version,
)