Deprecated old backports and typing aliases (#114883)
This commit is contained in:
parent
8e645c9b32
commit
8324fd5d1d
9 changed files with 250 additions and 30 deletions
|
@ -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())
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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__) == {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
37
tests/helpers/test_typing.py
Normal file
37
tests/helpers/test_typing.py
Normal 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
41
tests/test_backports.py
Normal 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,
|
||||
)
|
Loading…
Add table
Reference in a new issue