From db985925c47697f391253d31b9ecfa363a1f9f6d Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Tue, 19 Dec 2023 19:22:13 +0100 Subject: [PATCH] Deprecate deprecated automation constants (#106067) --- .../components/automation/__init__.py | 23 +++++++-- tests/common.py | 47 +++++++++++++++++-- tests/components/automation/test_init.py | 22 +++++++++ tests/components/binary_sensor/test_init.py | 8 +--- .../test_constant_deprecation/__init__.py | 9 ++++ .../binary_sensor.py | 12 ----- .../test_constant_deprecation/util.py | 11 ----- 7 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 tests/testing_config/custom_components/test_constant_deprecation/__init__.py delete mode 100644 tests/testing_config/custom_components/test_constant_deprecation/binary_sensor.py delete mode 100644 tests/testing_config/custom_components/test_constant_deprecation/util.py diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 84f7f3aca52..4e6fa477ed2 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -5,6 +5,7 @@ from abc import ABC, abstractmethod import asyncio from collections.abc import Callable, Mapping from dataclasses import dataclass +from functools import partial import logging from typing import Any, Protocol, cast @@ -55,6 +56,11 @@ from homeassistant.exceptions import ( ) from homeassistant.helpers import condition import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.deprecation import ( + DeprecatedConstant, + check_if_deprecated_constant, + dir_with_deprecated_constants, +) from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue @@ -130,9 +136,20 @@ class IfAction(Protocol): # AutomationActionType, AutomationTriggerData, # and AutomationTriggerInfo are deprecated as of 2022.9. -AutomationActionType = TriggerActionType -AutomationTriggerData = TriggerData -AutomationTriggerInfo = TriggerInfo +# Can be removed in 2025.1 +_DEPRECATED_AutomationActionType = DeprecatedConstant( + TriggerActionType, "TriggerActionType", "2025.1" +) +_DEPRECATED_AutomationTriggerData = DeprecatedConstant( + TriggerData, "TriggerData", "2025.1" +) +_DEPRECATED_AutomationTriggerInfo = DeprecatedConstant( + TriggerInfo, "TriggerInfo", "2025.1" +) + +# Both 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=globals()) @bind_hass diff --git a/tests/common.py b/tests/common.py index 05bddec203c..c402f2aa661 100644 --- a/tests/common.py +++ b/tests/common.py @@ -91,6 +91,10 @@ from homeassistant.util.unit_system import METRIC_SYSTEM import homeassistant.util.uuid as uuid_util import homeassistant.util.yaml.loader as yaml_loader +from tests.testing_config.custom_components.test_constant_deprecation import ( + import_deprecated_costant, +) + _LOGGER = logging.getLogger(__name__) INSTANCES = [] CLIENT_ID = "https://example.com/app" @@ -1465,24 +1469,57 @@ def async_mock_cloud_connection_status(hass: HomeAssistant, connected: bool) -> async_dispatcher_send(hass, SIGNAL_CLOUD_CONNECTION_STATE, state) -def validate_deprecated_constant( +def import_and_test_deprecated_constant_enum( 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__().""" + """Import and test deprecated constant replaced by a enum. + + - Import deprecated enum + - Assert value is the same as the replacement + - Assert a warning is logged + - Assert the deprecated constant is included in the modules.__dir__() + """ + import_and_test_deprecated_constant( + caplog, + module, + constant_prefix + replacement.name, + f"{replacement.__class__.__name__}.{replacement.name}", + replacement, + breaks_in_ha_version, + ) + + +def import_and_test_deprecated_constant( + caplog: pytest.LogCaptureFixture, + module: ModuleType, + constant_name: str, + replacement_name: str, + replacement: Any, + breaks_in_ha_version: str, +) -> None: + """Import and test deprecated constant replaced by a value. + + - Import deprecated constant + - Assert value is the same as the replacement + - Assert a warning is logged + - Assert the deprecated constant is included in the modules.__dir__() + """ + value = import_deprecated_costant(module, constant_name) + assert value == replacement assert ( module.__name__, logging.WARNING, ( - f"{constant_prefix}{replacement.name} was used from test_constant_deprecation," + f"{constant_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 " + f"Use {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) + assert constant_name in dir(module) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 359303c51fd..a2f2dfbf907 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -2,6 +2,7 @@ import asyncio from datetime import timedelta import logging +from typing import Any from unittest.mock import Mock, patch import pytest @@ -46,6 +47,7 @@ from homeassistant.helpers.script import ( SCRIPT_MODE_SINGLE, _async_stop_scripts_at_shutdown, ) +from homeassistant.helpers.trigger import TriggerActionType, TriggerData, TriggerInfo from homeassistant.setup import async_setup_component from homeassistant.util import yaml import homeassistant.util.dt as dt_util @@ -57,6 +59,7 @@ from tests.common import ( async_capture_events, async_fire_time_changed, async_mock_service, + import_and_test_deprecated_constant, mock_restore_cache, ) from tests.components.logbook.common import MockRow, mock_humanify @@ -2564,3 +2567,22 @@ async def test_websocket_config( msg = await client.receive_json() assert not msg["success"] assert msg["error"]["code"] == "not_found" + + +@pytest.mark.parametrize( + ("constant_name", "replacement"), + [ + ("AutomationActionType", TriggerActionType), + ("AutomationTriggerData", TriggerData), + ("AutomationTriggerInfo", TriggerInfo), + ], +) +def test_deprecated_constants( + caplog: pytest.LogCaptureFixture, + constant_name: str, + replacement: Any, +) -> None: + """Test deprecated binary sensor device classes.""" + import_and_test_deprecated_constant( + caplog, automation, constant_name, replacement.__name__, replacement, "2025.1" + ) diff --git a/tests/components/binary_sensor/test_init.py b/tests/components/binary_sensor/test_init.py index ac957818be9..014722d94a4 100644 --- a/tests/components/binary_sensor/test_init.py +++ b/tests/components/binary_sensor/test_init.py @@ -14,15 +14,12 @@ from tests.common import ( MockConfigEntry, MockModule, MockPlatform, + import_and_test_deprecated_constant_enum, mock_config_flow, mock_integration, mock_platform, - validate_deprecated_constant, ) from tests.testing_config.custom_components.test.binary_sensor import MockBinarySensor -from tests.testing_config.custom_components.test_constant_deprecation.binary_sensor import ( - import_deprecated, -) TEST_DOMAIN = "test" @@ -209,7 +206,6 @@ def test_deprecated_constant_device_class( device_class: binary_sensor.BinarySensorDeviceClass, ) -> None: """Test deprecated binary sensor device classes.""" - import_deprecated(device_class) - validate_deprecated_constant( + import_and_test_deprecated_constant_enum( caplog, binary_sensor, device_class, "DEVICE_CLASS_", "2025.1" ) diff --git a/tests/testing_config/custom_components/test_constant_deprecation/__init__.py b/tests/testing_config/custom_components/test_constant_deprecation/__init__.py new file mode 100644 index 00000000000..4367cbed7b1 --- /dev/null +++ b/tests/testing_config/custom_components/test_constant_deprecation/__init__.py @@ -0,0 +1,9 @@ +"""Test deprecated constants custom integration.""" + +from types import ModuleType +from typing import Any + + +def import_deprecated_costant(module: ModuleType, constant_name: str) -> Any: + """Import and return deprecated constant.""" + return getattr(module, constant_name) diff --git a/tests/testing_config/custom_components/test_constant_deprecation/binary_sensor.py b/tests/testing_config/custom_components/test_constant_deprecation/binary_sensor.py deleted file mode 100644 index dda4d4f83f7..00000000000 --- a/tests/testing_config/custom_components/test_constant_deprecation/binary_sensor.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Test deprecated binary sensor device classes.""" -from functools import partial - -from homeassistant.components import binary_sensor - -from .util import import_and_test_deprecated_costant - -import_deprecated = partial( - import_and_test_deprecated_costant, - module=binary_sensor, - constant_prefix="DEVICE_CLASS_", -) diff --git a/tests/testing_config/custom_components/test_constant_deprecation/util.py b/tests/testing_config/custom_components/test_constant_deprecation/util.py deleted file mode 100644 index 126bf8a7359..00000000000 --- a/tests/testing_config/custom_components/test_constant_deprecation/util.py +++ /dev/null @@ -1,11 +0,0 @@ -"""util module for test_constant_deprecation tests.""" - -from enum import Enum -from types import ModuleType - - -def import_and_test_deprecated_costant( - replacement: Enum, module: ModuleType, constant_prefix: str -) -> None: - """Import and test deprecated constant.""" - assert getattr(module, constant_prefix + replacement.name) == replacement