Rename UnitOfConductivity enum members (#127919)

* Rename UnitOfConductivity enum members

* Update test snapshots
This commit is contained in:
Erik Montnemery 2024-10-08 14:44:01 +02:00 committed by GitHub
parent 0c0ff855b1
commit 0956dbb578
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 331 additions and 15 deletions

View file

@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Final
from .helpers.deprecation import (
DeprecatedConstant,
DeprecatedConstantEnum,
EnumWithDeprecatedMembers,
all_with_deprecated_constants,
check_if_deprecated_constant,
dir_with_deprecated_constants,
@ -1177,20 +1178,35 @@ _DEPRECATED_MASS_POUNDS: Final = DeprecatedConstantEnum(
"""Deprecated: please use UnitOfMass.POUNDS"""
# Conductivity units
class UnitOfConductivity(StrEnum):
class UnitOfConductivity(
StrEnum,
metaclass=EnumWithDeprecatedMembers,
deprecated={
"SIEMENS": ("SIEMENS_PER_CM", "2025.11.0"),
"MICROSIEMENS": ("MICROSIEMENS_PER_CM", "2025.11.0"),
"MILLISIEMENS": ("MILLISIEMENS_PER_CM", "2025.11.0"),
},
):
"""Conductivity units."""
SIEMENS_PER_CM = "S/cm"
MICROSIEMENS_PER_CM = "µS/cm"
MILLISIEMENS_PER_CM = "mS/cm"
# Deprecated aliases
SIEMENS = "S/cm"
"""Deprecated: Please use UnitOfConductivity.SIEMENS_PER_CM"""
MICROSIEMENS = "µS/cm"
"""Deprecated: Please use UnitOfConductivity.MICROSIEMENS_PER_CM"""
MILLISIEMENS = "mS/cm"
"""Deprecated: Please use UnitOfConductivity.MILLISIEMENS_PER_CM"""
_DEPRECATED_CONDUCTIVITY: Final = DeprecatedConstantEnum(
UnitOfConductivity.MICROSIEMENS,
"2025.6",
UnitOfConductivity.MICROSIEMENS_PER_CM,
"2025.11",
)
"""Deprecated: please use UnitOfConductivity.MICROSIEMENS"""
"""Deprecated: please use UnitOfConductivity.MICROSIEMENS_PER_CM"""
# Light units
LIGHT_LUX: Final = "lx"

View file

@ -3,7 +3,7 @@
from __future__ import annotations
from collections.abc import Callable
from enum import Enum
from enum import Enum, EnumType, _EnumDict
import functools
import inspect
import logging
@ -338,3 +338,35 @@ def all_with_deprecated_constants(module_globals: dict[str, Any]) -> list[str]:
for name in module_globals_keys
if name.startswith(_PREFIX_DEPRECATED)
]
class EnumWithDeprecatedMembers(EnumType):
"""Enum with deprecated members."""
def __new__(
mcs, # noqa: N804 ruff bug, ruff does not understand this is a metaclass
cls: str,
bases: tuple[type, ...],
classdict: _EnumDict,
*,
deprecated: dict[str, tuple[str, str]],
**kwds: Any,
) -> Any:
"""Create a new class."""
classdict["__deprecated__"] = deprecated
return super().__new__(mcs, cls, bases, classdict, **kwds)
def __getattribute__(cls, name: str) -> Any:
"""Warn if accessing a deprecated member."""
deprecated = super().__getattribute__("__deprecated__")
if name in deprecated:
_print_deprecation_warning_internal(
f"{cls.__name__}.{name}",
cls.__module__,
f"{cls.__name__}.{deprecated[name][0]}",
"enum member",
"used",
deprecated[name][1],
log_when_no_integration_is_found=False,
)
return super().__getattribute__(name)

View file

@ -178,9 +178,9 @@ class ConductivityConverter(BaseUnitConverter):
UNIT_CLASS = "conductivity"
_UNIT_CONVERSION: dict[str | None, float] = {
UnitOfConductivity.MICROSIEMENS: 1,
UnitOfConductivity.MILLISIEMENS: 1e-3,
UnitOfConductivity.SIEMENS: 1e-6,
UnitOfConductivity.MICROSIEMENS_PER_CM: 1,
UnitOfConductivity.MILLISIEMENS_PER_CM: 1e-3,
UnitOfConductivity.SIEMENS_PER_CM: 1e-6,
}
VALID_UNITS = set(UnitOfConductivity)

View file

@ -421,7 +421,7 @@
'supported_features': 0,
'translation_key': 'salinity',
'unique_id': 'ce5f5431554d101905d31797e1232da8-0-salinity',
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS: 'mS/cm'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS_PER_CM: 'mS/cm'>,
})
# ---
# name: test_all_entities[sensor.gummibaum_salinity-state]
@ -430,7 +430,7 @@
'device_class': 'conductivity',
'friendly_name': 'Gummibaum Salinity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS: 'mS/cm'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS_PER_CM: 'mS/cm'>,
}),
'context': <ANY>,
'entity_id': 'sensor.gummibaum_salinity',
@ -1087,7 +1087,7 @@
'supported_features': 0,
'translation_key': 'salinity',
'unique_id': 'ce5f5431554d101905d31797e1232da8-1-salinity',
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS: 'mS/cm'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS_PER_CM: 'mS/cm'>,
})
# ---
# name: test_all_entities[sensor.kakaobaum_salinity-state]
@ -1096,7 +1096,7 @@
'device_class': 'conductivity',
'friendly_name': 'Kakaobaum Salinity',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS: 'mS/cm'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS_PER_CM: 'mS/cm'>,
}),
'context': <ANY>,
'entity_id': 'sensor.kakaobaum_salinity',

View file

@ -13,6 +13,7 @@ from homeassistant.helpers.deprecation import (
DeprecatedAlias,
DeprecatedConstant,
DeprecatedConstantEnum,
EnumWithDeprecatedMembers,
check_if_deprecated_constant,
deprecated_class,
deprecated_function,
@ -520,3 +521,119 @@ def test_dir_with_deprecated_constants(
) -> None:
"""Test dir() with deprecated constants."""
assert dir_with_deprecated_constants([*module_globals.keys()]) == expected
@pytest.mark.parametrize(
("module_name", "extra_extra_msg"),
[
("homeassistant.components.hue.light", ""), # builtin integration
(
"config.custom_components.hue.light",
", please report it to the author of the 'hue' custom integration",
), # custom component integration
],
)
def test_enum_with_deprecated_members(
caplog: pytest.LogCaptureFixture,
module_name: str,
extra_extra_msg: str,
) -> None:
"""Test EnumWithDeprecatedMembers."""
filename = f"/home/paulus/{module_name.replace('.', '/')}.py"
class TestEnum(
StrEnum,
metaclass=EnumWithDeprecatedMembers,
deprecated={
"CATS": ("CATS_PER_CM", "2025.11.0"),
"DOGS": ("DOGS_PER_CM", None),
},
):
"""Zoo units."""
CATS_PER_CM = "cats/cm"
DOGS_PER_CM = "dogs/cm"
CATS = "cats/cm"
DOGS = "dogs/cm"
# mock sys.modules for homeassistant/helpers/frame.py#get_integration_frame
with (
patch.dict(sys.modules, {module_name: Mock(__file__=filename)}),
patch(
"homeassistant.helpers.frame.linecache.getline",
return_value="await session.close()",
),
patch(
"homeassistant.helpers.frame.get_current_frame",
return_value=extract_stack_to_frame(
[
Mock(
filename="/home/paulus/homeassistant/core.py",
lineno="23",
line="do_something()",
),
Mock(
filename=filename,
lineno="23",
line="await session.close()",
),
Mock(
filename="/home/paulus/aiohue/lights.py",
lineno="2",
line="something()",
),
]
),
),
):
TestEnum.CATS # noqa: B018
TestEnum.DOGS # noqa: B018
assert len(caplog.record_tuples) == 2
assert (
"tests.helpers.test_deprecation",
logging.WARNING,
(
"TestEnum.CATS was used from hue, this is a deprecated enum member which "
"will be removed in HA Core 2025.11.0. Use TestEnum.CATS_PER_CM instead"
f"{extra_extra_msg}"
),
) in caplog.record_tuples
assert (
"tests.helpers.test_deprecation",
logging.WARNING,
(
"TestEnum.DOGS was used from hue, this is a deprecated enum member. Use "
f"TestEnum.DOGS_PER_CM instead{extra_extra_msg}"
),
) in caplog.record_tuples
def test_enum_with_deprecated_members_integration_not_found(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test check_if_deprecated_constant."""
class TestEnum(
StrEnum,
metaclass=EnumWithDeprecatedMembers,
deprecated={
"CATS": ("CATS_PER_CM", "2025.11.0"),
"DOGS": ("DOGS_PER_CM", None),
},
):
"""Zoo units."""
CATS_PER_CM = "cats/cm"
DOGS_PER_CM = "dogs/cm"
CATS = "cats/cm"
DOGS = "dogs/cm"
with patch(
"homeassistant.helpers.frame.get_current_frame",
side_effect=MissingIntegrationFrame,
):
TestEnum.CATS # noqa: B018
TestEnum.DOGS # noqa: B018
assert len(caplog.record_tuples) == 0

View file

@ -1,6 +1,9 @@
"""Test const module."""
from enum import Enum
import logging
import sys
from unittest.mock import Mock, patch
import pytest
@ -8,6 +11,7 @@ from homeassistant import const
from homeassistant.components import lock, sensor
from .common import (
extract_stack_to_frame,
help_test_all,
import_and_test_deprecated_constant,
import_and_test_deprecated_constant_enum,
@ -212,3 +216,78 @@ def test_deprecated_constants_lock(
import_and_test_deprecated_constant_enum(
caplog, const, enum, constant_prefix, remove_in_version
)
def test_deprecated_unit_of_conductivity_alias() -> None:
"""Test UnitOfConductivity deprecation."""
# Test the deprecated members are aliases
assert set(const.UnitOfConductivity) == {"S/cm", "µS/cm", "mS/cm"}
def test_deprecated_unit_of_conductivity_members(
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test UnitOfConductivity deprecation."""
module_name = "config.custom_components.hue.light"
filename = f"/home/paulus/{module_name.replace('.', '/')}.py"
with (
patch.dict(sys.modules, {module_name: Mock(__file__=filename)}),
patch(
"homeassistant.helpers.frame.linecache.getline",
return_value="await session.close()",
),
patch(
"homeassistant.helpers.frame.get_current_frame",
return_value=extract_stack_to_frame(
[
Mock(
filename="/home/paulus/homeassistant/core.py",
lineno="23",
line="do_something()",
),
Mock(
filename=filename,
lineno="23",
line="await session.close()",
),
Mock(
filename="/home/paulus/aiohue/lights.py",
lineno="2",
line="something()",
),
]
),
),
):
const.UnitOfConductivity.SIEMENS # noqa: B018
const.UnitOfConductivity.MICROSIEMENS # noqa: B018
const.UnitOfConductivity.MILLISIEMENS # noqa: B018
assert len(caplog.record_tuples) == 3
def deprecation_message(member: str, replacement: str) -> str:
return (
f"UnitOfConductivity.{member} was used from hue, this is a deprecated enum "
"member which will be removed in HA Core 2025.11.0. Use UnitOfConductivity."
f"{replacement} instead, please report it to the author of the 'hue' custom"
" integration"
)
assert (
const.__name__,
logging.WARNING,
deprecation_message("SIEMENS", "SIEMENS_PER_CM"),
) in caplog.record_tuples
assert (
const.__name__,
logging.WARNING,
deprecation_message("MICROSIEMENS", "MICROSIEMENS_PER_CM"),
) in caplog.record_tuples
assert (
const.__name__,
logging.WARNING,
deprecation_message("MILLISIEMENS", "MILLISIEMENS_PER_CM"),
) in caplog.record_tuples

View file

@ -81,8 +81,8 @@ _ALL_CONVERTERS: dict[type[BaseUnitConverter], list[str | None]] = {
# Dict containing all converters with a corresponding unit ratio.
_GET_UNIT_RATIO: dict[type[BaseUnitConverter], tuple[str | None, str | None, float]] = {
ConductivityConverter: (
UnitOfConductivity.MICROSIEMENS,
UnitOfConductivity.MILLISIEMENS,
UnitOfConductivity.MICROSIEMENS_PER_CM,
UnitOfConductivity.MILLISIEMENS_PER_CM,
1000,
),
DataRateConverter: (
@ -131,12 +131,84 @@ _CONVERTED_VALUE: dict[
type[BaseUnitConverter], list[tuple[float, str | None, float, str | None]]
] = {
ConductivityConverter: [
# Deprecated to deprecated
(5, UnitOfConductivity.SIEMENS, 5e3, UnitOfConductivity.MILLISIEMENS),
(5, UnitOfConductivity.SIEMENS, 5e6, UnitOfConductivity.MICROSIEMENS),
(5, UnitOfConductivity.MILLISIEMENS, 5e3, UnitOfConductivity.MICROSIEMENS),
(5, UnitOfConductivity.MILLISIEMENS, 5e-3, UnitOfConductivity.SIEMENS),
(5e6, UnitOfConductivity.MICROSIEMENS, 5e3, UnitOfConductivity.MILLISIEMENS),
(5e6, UnitOfConductivity.MICROSIEMENS, 5, UnitOfConductivity.SIEMENS),
# Deprecated to new
(5, UnitOfConductivity.SIEMENS, 5e3, UnitOfConductivity.MILLISIEMENS_PER_CM),
(5, UnitOfConductivity.SIEMENS, 5e6, UnitOfConductivity.MICROSIEMENS_PER_CM),
(
5,
UnitOfConductivity.MILLISIEMENS,
5e3,
UnitOfConductivity.MICROSIEMENS_PER_CM,
),
(5, UnitOfConductivity.MILLISIEMENS, 5e-3, UnitOfConductivity.SIEMENS_PER_CM),
(
5e6,
UnitOfConductivity.MICROSIEMENS,
5e3,
UnitOfConductivity.MILLISIEMENS_PER_CM,
),
(5e6, UnitOfConductivity.MICROSIEMENS, 5, UnitOfConductivity.SIEMENS_PER_CM),
# New to deprecated
(5, UnitOfConductivity.SIEMENS_PER_CM, 5e3, UnitOfConductivity.MILLISIEMENS),
(5, UnitOfConductivity.SIEMENS_PER_CM, 5e6, UnitOfConductivity.MICROSIEMENS),
(
5,
UnitOfConductivity.MILLISIEMENS_PER_CM,
5e3,
UnitOfConductivity.MICROSIEMENS,
),
(5, UnitOfConductivity.MILLISIEMENS_PER_CM, 5e-3, UnitOfConductivity.SIEMENS),
(
5e6,
UnitOfConductivity.MICROSIEMENS_PER_CM,
5e3,
UnitOfConductivity.MILLISIEMENS,
),
(5e6, UnitOfConductivity.MICROSIEMENS_PER_CM, 5, UnitOfConductivity.SIEMENS),
# New to new
(
5,
UnitOfConductivity.SIEMENS_PER_CM,
5e3,
UnitOfConductivity.MILLISIEMENS_PER_CM,
),
(
5,
UnitOfConductivity.SIEMENS_PER_CM,
5e6,
UnitOfConductivity.MICROSIEMENS_PER_CM,
),
(
5,
UnitOfConductivity.MILLISIEMENS_PER_CM,
5e3,
UnitOfConductivity.MICROSIEMENS_PER_CM,
),
(
5,
UnitOfConductivity.MILLISIEMENS_PER_CM,
5e-3,
UnitOfConductivity.SIEMENS_PER_CM,
),
(
5e6,
UnitOfConductivity.MICROSIEMENS_PER_CM,
5e3,
UnitOfConductivity.MILLISIEMENS_PER_CM,
),
(
5e6,
UnitOfConductivity.MICROSIEMENS_PER_CM,
5,
UnitOfConductivity.SIEMENS_PER_CM,
),
],
DataRateConverter: [
(8e3, UnitOfDataRate.BITS_PER_SECOND, 8, UnitOfDataRate.KILOBITS_PER_SECOND),