Add dir_with_deprecated_constants function to deprecation helper (#106059)

This commit is contained in:
Robert Resch 2023-12-19 16:37:21 +01:00 committed by GitHub
parent 63136572a5
commit 0e0fd39603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 14 deletions

View File

@ -20,6 +20,7 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
from homeassistant.helpers.deprecation import ( from homeassistant.helpers.deprecation import (
DeprecatedConstantEnum, DeprecatedConstantEnum,
check_if_deprecated_constant, check_if_deprecated_constant,
dir_with_deprecated_constants,
) )
from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
@ -211,7 +212,9 @@ _DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
BinarySensorDeviceClass.WINDOW, "2025.1" BinarySensorDeviceClass.WINDOW, "2025.1"
) )
# Both can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals()) __getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
# mypy: disallow-any-generics # mypy: disallow-any-generics

View File

@ -237,6 +237,9 @@ class DeprecatedConstantEnum(NamedTuple):
breaks_in_ha_version: str | None breaks_in_ha_version: str | None
_PREFIX_DEPRECATED = "_DEPRECATED_"
def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> Any: def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> Any:
"""Check if the not found name is a deprecated constant. """Check if the not found name is a deprecated constant.
@ -245,7 +248,7 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
""" """
module_name = module_globals.get("__name__") module_name = module_globals.get("__name__")
logger = logging.getLogger(module_name) logger = logging.getLogger(module_name)
if (deprecated_const := module_globals.get(f"_DEPRECATED_{name}")) is None: if (deprecated_const := module_globals.get(_PREFIX_DEPRECATED + name)) is None:
raise AttributeError(f"Module {module_name!r} has no attribute {name!r}") raise AttributeError(f"Module {module_name!r} has no attribute {name!r}")
if isinstance(deprecated_const, DeprecatedConstant): if isinstance(deprecated_const, DeprecatedConstant):
value = deprecated_const.value value = deprecated_const.value
@ -259,7 +262,7 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
breaks_in_ha_version = deprecated_const.breaks_in_ha_version breaks_in_ha_version = deprecated_const.breaks_in_ha_version
else: else:
msg = ( msg = (
f"Value of _DEPRECATED_{name!r} is an instance of {type(deprecated_const)} " f"Value of {_PREFIX_DEPRECATED}{name!r} is an instance of {type(deprecated_const)} "
"but an instance of DeprecatedConstant or DeprecatedConstantEnum is required" "but an instance of DeprecatedConstant or DeprecatedConstantEnum is required"
) )
@ -279,3 +282,12 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
breaks_in_ha_version, breaks_in_ha_version,
) )
return value return value
def dir_with_deprecated_constants(module_globals: dict[str, Any]) -> list[str]:
"""Return dir() with deprecated constants."""
return list(module_globals) + [
name.removeprefix(_PREFIX_DEPRECATED)
for name in module_globals
if name.startswith(_PREFIX_DEPRECATED)
]

View File

@ -6,6 +6,7 @@ from collections import OrderedDict
from collections.abc import Generator, Mapping, Sequence from collections.abc import Generator, Mapping, Sequence
from contextlib import contextmanager from contextlib import contextmanager
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from enum import Enum
import functools as ft import functools as ft
from functools import lru_cache from functools import lru_cache
from io import StringIO from io import StringIO
@ -15,10 +16,12 @@ import os
import pathlib import pathlib
import threading import threading
import time import time
from types import ModuleType
from typing import Any, NoReturn from typing import Any, NoReturn
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
from aiohttp.test_utils import unused_port as get_test_instance_port # noqa: F401 from aiohttp.test_utils import unused_port as get_test_instance_port # noqa: F401
import pytest
import voluptuous as vol import voluptuous as vol
from homeassistant import auth, bootstrap, config_entries, loader from homeassistant import auth, bootstrap, config_entries, loader
@ -1460,3 +1463,26 @@ def async_mock_cloud_connection_status(hass: HomeAssistant, connected: bool) ->
else: else:
state = CloudConnectionState.CLOUD_DISCONNECTED state = CloudConnectionState.CLOUD_DISCONNECTED
async_dispatcher_send(hass, SIGNAL_CLOUD_CONNECTION_STATE, state) async_dispatcher_send(hass, SIGNAL_CLOUD_CONNECTION_STATE, state)
def validate_deprecated_constant(
caplog: pytest.LogCaptureFixture,
module: ModuleType,
replacement: Enum,
constant_prefix: str,
breaks_in_ha_version: str,
) -> None:
"""Validate deprecated constant creates a log entry and is included in the modules.__dir__()."""
assert (
module.__name__,
logging.WARNING,
(
f"{constant_prefix}{replacement.name} was used from test_constant_deprecation,"
f" this is a deprecated constant which will be removed in HA Core {breaks_in_ha_version}. "
f"Use {replacement.__class__.__name__}.{replacement.name} instead, please report "
"it to the author of the 'test_constant_deprecation' custom integration"
),
) in caplog.record_tuples
# verify deprecated constant is included in dir()
assert f"{constant_prefix}{replacement.name}" in dir(module)

View File

@ -1,6 +1,5 @@
"""The tests for the Binary sensor component.""" """The tests for the Binary sensor component."""
from collections.abc import Generator from collections.abc import Generator
import logging
from unittest import mock from unittest import mock
import pytest import pytest
@ -18,6 +17,7 @@ from tests.common import (
mock_config_flow, mock_config_flow,
mock_integration, mock_integration,
mock_platform, mock_platform,
validate_deprecated_constant,
) )
from tests.testing_config.custom_components.test.binary_sensor import MockBinarySensor from tests.testing_config.custom_components.test.binary_sensor import MockBinarySensor
from tests.testing_config.custom_components.test_constant_deprecation.binary_sensor import ( from tests.testing_config.custom_components.test_constant_deprecation.binary_sensor import (
@ -210,14 +210,6 @@ def test_deprecated_constant_device_class(
) -> None: ) -> None:
"""Test deprecated binary sensor device classes.""" """Test deprecated binary sensor device classes."""
import_deprecated(device_class) import_deprecated(device_class)
validate_deprecated_constant(
assert ( caplog, binary_sensor, device_class, "DEVICE_CLASS_", "2025.1"
"homeassistant.components.binary_sensor", )
logging.WARNING,
(
f"DEVICE_CLASS_{device_class.name} was used from test_constant_deprecation,"
" this is a deprecated constant which will be removed in HA Core 2025.1. "
f"Use BinarySensorDeviceClass.{device_class.name} instead, please report "
"it to the author of the 'test_constant_deprecation' custom integration"
),
) in caplog.record_tuples

View File

@ -1,6 +1,7 @@
"""Test deprecation helpers.""" """Test deprecation helpers."""
import logging import logging
import sys import sys
from typing import Any
from unittest.mock import MagicMock, Mock, patch from unittest.mock import MagicMock, Mock, patch
import pytest import pytest
@ -13,6 +14,7 @@ from homeassistant.helpers.deprecation import (
deprecated_class, deprecated_class,
deprecated_function, deprecated_function,
deprecated_substitute, deprecated_substitute,
dir_with_deprecated_constants,
get_deprecated, get_deprecated,
) )
@ -341,3 +343,21 @@ def test_test_check_if_deprecated_constant_invalid(
check_if_deprecated_constant(name, module_globals) check_if_deprecated_constant(name, module_globals)
assert (module_name, logging.DEBUG, excepted_msg) in caplog.record_tuples assert (module_name, logging.DEBUG, excepted_msg) in caplog.record_tuples
@pytest.mark.parametrize(
("module_global", "expected"),
[
({"CONSTANT": 1}, ["CONSTANT"]),
({"_DEPRECATED_CONSTANT": 1}, ["_DEPRECATED_CONSTANT", "CONSTANT"]),
(
{"_DEPRECATED_CONSTANT": 1, "SOMETHING": 2},
["_DEPRECATED_CONSTANT", "SOMETHING", "CONSTANT"],
),
],
)
def test_dir_with_deprecated_constants(
module_global: dict[str, Any], expected: list[str]
) -> None:
"""Test dir() with deprecated constants."""
assert dir_with_deprecated_constants(module_global) == expected