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 (
DeprecatedConstantEnum,
check_if_deprecated_constant,
dir_with_deprecated_constants,
)
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
@ -211,7 +212,9 @@ _DEPRECATED_DEVICE_CLASS_WINDOW = DeprecatedConstantEnum(
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())
__dir__ = partial(dir_with_deprecated_constants, module_globals=globals())
# mypy: disallow-any-generics

View file

@ -237,6 +237,9 @@ class DeprecatedConstantEnum(NamedTuple):
breaks_in_ha_version: str | None
_PREFIX_DEPRECATED = "_DEPRECATED_"
def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> Any:
"""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__")
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}")
if isinstance(deprecated_const, DeprecatedConstant):
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
else:
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"
)
@ -279,3 +282,12 @@ def check_if_deprecated_constant(name: str, module_globals: dict[str, Any]) -> A
breaks_in_ha_version,
)
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 contextlib import contextmanager
from datetime import UTC, datetime, timedelta
from enum import Enum
import functools as ft
from functools import lru_cache
from io import StringIO
@ -15,10 +16,12 @@ import os
import pathlib
import threading
import time
from types import ModuleType
from typing import Any, NoReturn
from unittest.mock import AsyncMock, Mock, patch
from aiohttp.test_utils import unused_port as get_test_instance_port # noqa: F401
import pytest
import voluptuous as vol
from homeassistant import auth, bootstrap, config_entries, loader
@ -1460,3 +1463,26 @@ def async_mock_cloud_connection_status(hass: HomeAssistant, connected: bool) ->
else:
state = CloudConnectionState.CLOUD_DISCONNECTED
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."""
from collections.abc import Generator
import logging
from unittest import mock
import pytest
@ -18,6 +17,7 @@ from tests.common import (
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 (
@ -210,14 +210,6 @@ def test_deprecated_constant_device_class(
) -> None:
"""Test deprecated binary sensor device classes."""
import_deprecated(device_class)
assert (
"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
validate_deprecated_constant(
caplog, binary_sensor, device_class, "DEVICE_CLASS_", "2025.1"
)

View file

@ -1,6 +1,7 @@
"""Test deprecation helpers."""
import logging
import sys
from typing import Any
from unittest.mock import MagicMock, Mock, patch
import pytest
@ -13,6 +14,7 @@ from homeassistant.helpers.deprecation import (
deprecated_class,
deprecated_function,
deprecated_substitute,
dir_with_deprecated_constants,
get_deprecated,
)
@ -341,3 +343,21 @@ def test_test_check_if_deprecated_constant_invalid(
check_if_deprecated_constant(name, module_globals)
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