From fc3d7d463e0d88019661633981f8c9528fa84280 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 26 Jun 2026 12:51:09 +0000 Subject: [PATCH 1/5] Use consistent import style for `frequenz.core.enum` Two import styles coexisted for `frequenz.core.enum`: some modules used `from frequenz.core import enum as core_enum` and qualified every symbol (`core_enum.unique`, `core_enum.Enum`, `core_enum.deprecated_member`), while `grid/_delivery_area.py` already used the direct named-import form `from frequenz.core.enum import Enum, deprecated_member, unique`. Switch the remaining consumers to the same named-import form so the codebase speaks a single style end-to-end. As a side effect, `metrics/_sample.py` no longer needs `import enum` because the stdlib `enum.unique`/`enum.Enum` used by `AggregationMethod` are now sourced from `frequenz.core.enum` along with everything else. This is a pure mechanical refactor: no enum member, value, decorator semantics, deprecation message string, or public symbol changes; only the import statement and the unqualified references it enables. No release note is needed since there is no public surface change. Signed-off-by: Leandro Lucarella --- src/frequenz/client/common/metrics/_metric.py | 8 ++-- src/frequenz/client/common/metrics/_sample.py | 13 +++-- .../electrical_components/_battery.py | 14 +++--- .../electrical_components/_category.py | 48 +++++++++---------- .../electrical_components/_ev_charger.py | 16 +++---- .../electrical_components/_inverter.py | 16 +++---- 6 files changed, 53 insertions(+), 62 deletions(-) diff --git a/src/frequenz/client/common/metrics/_metric.py b/src/frequenz/client/common/metrics/_metric.py index 53773d61..587684cd 100644 --- a/src/frequenz/client/common/metrics/_metric.py +++ b/src/frequenz/client/common/metrics/_metric.py @@ -3,11 +3,11 @@ """Supported metrics for microgrid components.""" -from frequenz.core import enum as core_enum +from frequenz.core.enum import Enum, deprecated_member, unique -@core_enum.unique -class Metric(core_enum.Enum): +@unique +class Metric(Enum): """List of supported metrics. Metric units are as follows: @@ -39,7 +39,7 @@ class Metric(core_enum.Enum): period, and therefore can be inconsistent. """ - UNSPECIFIED = core_enum.deprecated_member( + UNSPECIFIED = deprecated_member( 0, "Metric.UNSPECIFIED is deprecated; use the `int` value `0` " "instead if you really need to check for this low-level value.", diff --git a/src/frequenz/client/common/metrics/_sample.py b/src/frequenz/client/common/metrics/_sample.py index c58b3ab7..1728b595 100644 --- a/src/frequenz/client/common/metrics/_sample.py +++ b/src/frequenz/client/common/metrics/_sample.py @@ -3,22 +3,21 @@ """Definition to work with metric sample values.""" -import enum import warnings from collections.abc import Sequence from dataclasses import dataclass from datetime import datetime from typing import assert_never -from frequenz.core import enum as core_enum +from frequenz.core.enum import Enum, deprecated_member, unique from .._exception import UnrecognizedValueError, UnspecifiedValueError from ._bounds import Bounds from ._metric import Metric -@enum.unique -class AggregationMethod(enum.Enum): +@unique +class AggregationMethod(Enum): """The type of the aggregated value.""" AVG = "avg" @@ -70,11 +69,11 @@ def __str__(self) -> str: return f"avg:{self.avg}{extra_str}" -@core_enum.unique -class MetricConnectionCategory(core_enum.Enum): +@unique +class MetricConnectionCategory(Enum): """The categories of connections from which metrics can be obtained.""" - UNSPECIFIED = core_enum.deprecated_member( + UNSPECIFIED = deprecated_member( 0, "MetricConnectionCategory.UNSPECIFIED is deprecated; use the `int` value `0` " "instead if you really need to check for this low-level value.", diff --git a/src/frequenz/client/common/microgrid/electrical_components/_battery.py b/src/frequenz/client/common/microgrid/electrical_components/_battery.py index 4d3afaf1..f12d48b8 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_battery.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_battery.py @@ -8,7 +8,7 @@ from typing import Any, Self, TypeAlias import typing_extensions -from frequenz.core import enum as core_enum +from frequenz.core.enum import Enum, deprecated_member, unique from ._electrical_component import ElectricalComponent @@ -36,19 +36,17 @@ def _battery_type_member_message(name: str) -> str: @typing_extensions.deprecated(_BATTERY_TYPE_DEPRECATION_MESSAGE) -@core_enum.unique -class BatteryType(core_enum.Enum): +@unique +class BatteryType(Enum): """The known types of batteries.""" - UNSPECIFIED = core_enum.deprecated_member( - 0, _battery_type_member_message("UNSPECIFIED") - ) + UNSPECIFIED = deprecated_member(0, _battery_type_member_message("UNSPECIFIED")) """The battery type is unspecified.""" - LI_ION = core_enum.deprecated_member(1, _battery_type_member_message("LI_ION")) + LI_ION = deprecated_member(1, _battery_type_member_message("LI_ION")) """Lithium-ion (Li-ion) battery.""" - NA_ION = core_enum.deprecated_member(2, _battery_type_member_message("NA_ION")) + NA_ION = deprecated_member(2, _battery_type_member_message("NA_ION")) """Sodium-ion (Na-ion) battery.""" diff --git a/src/frequenz/client/common/microgrid/electrical_components/_category.py b/src/frequenz/client/common/microgrid/electrical_components/_category.py index 0b2227a8..1843f99d 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_category.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_category.py @@ -4,7 +4,7 @@ """Electrical component categories.""" import typing_extensions -from frequenz.core import enum as core_enum +from frequenz.core.enum import Enum, deprecated_member, unique _DEPRECATION_MESSAGE = ( "ElectricalComponentCategory is deprecated; use the ElectricalComponent class " @@ -30,77 +30,75 @@ def _member_message(name: str) -> str: @typing_extensions.deprecated(_DEPRECATION_MESSAGE) -@core_enum.unique -class ElectricalComponentCategory(core_enum.Enum): +@unique +class ElectricalComponentCategory(Enum): """Possible types of microgrid electrical component.""" - UNSPECIFIED = core_enum.deprecated_member(0, _member_message("UNSPECIFIED")) + UNSPECIFIED = deprecated_member(0, _member_message("UNSPECIFIED")) """The component category is unspecified. This should not be used.""" - GRID_CONNECTION_POINT = core_enum.deprecated_member( + GRID_CONNECTION_POINT = deprecated_member( 1, _member_message("GRID_CONNECTION_POINT") ) """The point where the local microgrid is connected to the grid.""" - METER = core_enum.deprecated_member(2, _member_message("METER")) + METER = deprecated_member(2, _member_message("METER")) """A meter, for measuring electrical metrics, e.g., current, voltage, etc.""" - INVERTER = core_enum.deprecated_member(3, _member_message("INVERTER")) + INVERTER = deprecated_member(3, _member_message("INVERTER")) """An inverter that converts DC to AC power and vice versa.""" - CONVERTER = core_enum.deprecated_member(4, _member_message("CONVERTER")) + CONVERTER = deprecated_member(4, _member_message("CONVERTER")) """An electricity converter, e.g., a DC-DC converter.""" - BATTERY = core_enum.deprecated_member(5, _member_message("BATTERY")) + BATTERY = deprecated_member(5, _member_message("BATTERY")) """A battery energy storage system.""" - EV_CHARGER = core_enum.deprecated_member(6, _member_message("EV_CHARGER")) + EV_CHARGER = deprecated_member(6, _member_message("EV_CHARGER")) """A station for charging electrical vehicles.""" - BREAKER = core_enum.deprecated_member(7, _member_message("BREAKER")) + BREAKER = deprecated_member(7, _member_message("BREAKER")) """A circuit breaker, providing protection and switching by disconnecting circuits.""" - PRECHARGER = core_enum.deprecated_member(8, _member_message("PRECHARGER")) + PRECHARGER = deprecated_member(8, _member_message("PRECHARGER")) """A precharger, used for preparing electrical circuits for switching on.""" - CHP = core_enum.deprecated_member(9, _member_message("CHP")) + CHP = deprecated_member(9, _member_message("CHP")) """A combined heat and power (CHP) plant. It generates electricity and useful heat from a single energy source. """ - ELECTROLYZER = core_enum.deprecated_member(10, _member_message("ELECTROLYZER")) + ELECTROLYZER = deprecated_member(10, _member_message("ELECTROLYZER")) """A device for splitting water into hydrogen and oxygen using electricity.""" - POWER_TRANSFORMER = core_enum.deprecated_member( - 11, _member_message("POWER_TRANSFORMER") - ) + POWER_TRANSFORMER = deprecated_member(11, _member_message("POWER_TRANSFORMER")) """A transformer, used for changing the voltage of electrical circuits.""" - HVAC = core_enum.deprecated_member(12, _member_message("HVAC")) + HVAC = deprecated_member(12, _member_message("HVAC")) """A heating, ventilation, and air conditioning (HVAC) system.""" - PLC = core_enum.deprecated_member(13, _member_message("PLC")) + PLC = deprecated_member(13, _member_message("PLC")) """A programmable logic controller (PLC).""" - CRYPTO_MINER = core_enum.deprecated_member(14, _member_message("CRYPTO_MINER")) + CRYPTO_MINER = deprecated_member(14, _member_message("CRYPTO_MINER")) """A device for mining cryptocurrencies.""" - STATIC_TRANSFER_SWITCH = core_enum.deprecated_member( + STATIC_TRANSFER_SWITCH = deprecated_member( 15, _member_message("STATIC_TRANSFER_SWITCH") ) """A static transfer switch, used for switching between power sources.""" - UNINTERRUPTIBLE_POWER_SUPPLY = core_enum.deprecated_member( + UNINTERRUPTIBLE_POWER_SUPPLY = deprecated_member( 16, _member_message("UNINTERRUPTIBLE_POWER_SUPPLY") ) """An uninterruptible power supply (UPS), used to provide backup power.""" - CAPACITOR_BANK = core_enum.deprecated_member(17, _member_message("CAPACITOR_BANK")) + CAPACITOR_BANK = deprecated_member(17, _member_message("CAPACITOR_BANK")) """A capacitor bank, used for power factor correction and reactive power compensation.""" - WIND_TURBINE = core_enum.deprecated_member(18, _member_message("WIND_TURBINE")) + WIND_TURBINE = deprecated_member(18, _member_message("WIND_TURBINE")) """A wind turbine, used to generate electricity from wind energy.""" - STEAM_BOILER = core_enum.deprecated_member(19, _member_message("STEAM_BOILER")) + STEAM_BOILER = deprecated_member(19, _member_message("STEAM_BOILER")) """A steam boiler, used to generate steam for heating or industrial processes.""" diff --git a/src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py b/src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py index 43b335d5..a870b485 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py @@ -8,7 +8,7 @@ from typing import Any, Self, TypeAlias import typing_extensions -from frequenz.core import enum as core_enum +from frequenz.core.enum import Enum, deprecated_member, unique from ._electrical_component import ElectricalComponent @@ -36,22 +36,20 @@ def _ev_charger_type_member_message(name: str) -> str: @typing_extensions.deprecated(_EV_CHARGER_TYPE_DEPRECATION_MESSAGE) -@core_enum.unique -class EvChargerType(core_enum.Enum): +@unique +class EvChargerType(Enum): """The known types of electric vehicle (EV) chargers.""" - UNSPECIFIED = core_enum.deprecated_member( - 0, _ev_charger_type_member_message("UNSPECIFIED") - ) + UNSPECIFIED = deprecated_member(0, _ev_charger_type_member_message("UNSPECIFIED")) """The type of the EV charger is unspecified.""" - AC = core_enum.deprecated_member(1, _ev_charger_type_member_message("AC")) + AC = deprecated_member(1, _ev_charger_type_member_message("AC")) """The EV charging station supports AC charging only.""" - DC = core_enum.deprecated_member(2, _ev_charger_type_member_message("DC")) + DC = deprecated_member(2, _ev_charger_type_member_message("DC")) """The EV charging station supports DC charging only.""" - HYBRID = core_enum.deprecated_member(3, _ev_charger_type_member_message("HYBRID")) + HYBRID = deprecated_member(3, _ev_charger_type_member_message("HYBRID")) """The EV charging station supports both AC and DC.""" diff --git a/src/frequenz/client/common/microgrid/electrical_components/_inverter.py b/src/frequenz/client/common/microgrid/electrical_components/_inverter.py index 0c7aafde..192a8765 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_inverter.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_inverter.py @@ -8,7 +8,7 @@ from typing import Any, Self, TypeAlias import typing_extensions -from frequenz.core import enum as core_enum +from frequenz.core.enum import Enum, deprecated_member, unique from ._electrical_component import ElectricalComponent @@ -36,22 +36,20 @@ def _inverter_type_member_message(name: str) -> str: @typing_extensions.deprecated(_INVERTER_TYPE_DEPRECATION_MESSAGE) -@core_enum.unique -class InverterType(core_enum.Enum): +@unique +class InverterType(Enum): """The known types of inverters.""" - UNSPECIFIED = core_enum.deprecated_member( - 0, _inverter_type_member_message("UNSPECIFIED") - ) + UNSPECIFIED = deprecated_member(0, _inverter_type_member_message("UNSPECIFIED")) """The type of the inverter is unspecified.""" - BATTERY = core_enum.deprecated_member(1, _inverter_type_member_message("BATTERY")) + BATTERY = deprecated_member(1, _inverter_type_member_message("BATTERY")) """The inverter is a battery inverter.""" - PV = core_enum.deprecated_member(2, _inverter_type_member_message("PV")) + PV = deprecated_member(2, _inverter_type_member_message("PV")) """The inverter is a PV inverter.""" - HYBRID = core_enum.deprecated_member(3, _inverter_type_member_message("HYBRID")) + HYBRID = deprecated_member(3, _inverter_type_member_message("HYBRID")) """The inverter is a hybrid inverter.""" From 35439c31b92bcb686cf73b81400a6ebf3db09f83 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 26 Jun 2026 13:02:05 +0000 Subject: [PATCH 2/5] Deprecate `ElectricalComponentDiagnosticCode.UNSPECIFIED` The `UNSPECIFIED` member is the zero / default protobuf enum value and is not a real diagnostic code. Mark it deprecated, so callers stop relying on it. Signed-off-by: Leandro Lucarella --- .../electrical_components/_diagnostic_code.py | 12 ++++++++---- .../proto/v1alpha8/test_diagnostic_code.py | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py b/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py index ddd3d2f6..092da57f 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py @@ -3,14 +3,18 @@ """Electrical component diagnostic codes.""" -import enum +from frequenz.core.enum import Enum, deprecated_member, unique -@enum.unique -class ElectricalComponentDiagnosticCode(enum.Enum): +@unique +class ElectricalComponentDiagnosticCode(Enum): """All diagnostics that can occur across electrical component categories.""" - UNSPECIFIED = 0 + UNSPECIFIED = deprecated_member( + 0, + "ElectricalComponentDiagnosticCode.UNSPECIFIED is deprecated; use the `int` value `0` " + "instead if you really need to check for this low-level value.", + ) """Default value. No specific error is specified.""" UNKNOWN = 1 diff --git a/tests/microgrid/electrical_components/proto/v1alpha8/test_diagnostic_code.py b/tests/microgrid/electrical_components/proto/v1alpha8/test_diagnostic_code.py index 2310ab5f..ba18abf9 100644 --- a/tests/microgrid/electrical_components/proto/v1alpha8/test_diagnostic_code.py +++ b/tests/microgrid/electrical_components/proto/v1alpha8/test_diagnostic_code.py @@ -25,3 +25,4 @@ class TestElectricalComponentDiagnosticCodeParity(EnumParityTest): name_prefix = "ELECTRICAL_COMPONENT_DIAGNOSTIC_CODE_" from_proto = staticmethod(electrical_component_diagnostic_code_from_proto) to_proto = staticmethod(electrical_component_diagnostic_code_to_proto) + deprecated_members = frozenset({"UNSPECIFIED"}) From b4a4fe5a56bd90a5e9884144aabb75a6086cb739 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 26 Jun 2026 13:03:41 +0000 Subject: [PATCH 3/5] Deprecate `ElectricalComponentStateCode.UNSPECIFIED` Mark `ElectricalComponentStateCode.UNSPECIFIED` as a deprecated member. Signed-off-by: Leandro Lucarella --- .../microgrid/electrical_components/_state_code.py | 12 ++++++++---- .../proto/v1alpha8/test_state_code.py | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/frequenz/client/common/microgrid/electrical_components/_state_code.py b/src/frequenz/client/common/microgrid/electrical_components/_state_code.py index bc293121..36bd66dd 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_state_code.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_state_code.py @@ -3,14 +3,18 @@ """Electrical component state codes.""" -import enum +from frequenz.core.enum import Enum, deprecated_member, unique -@enum.unique -class ElectricalComponentStateCode(enum.Enum): +@unique +class ElectricalComponentStateCode(Enum): """All possible states of a microgrid electrical component.""" - UNSPECIFIED = 0 + UNSPECIFIED = deprecated_member( + 0, + "ElectricalComponentStateCode.UNSPECIFIED is deprecated; use the `int` value `0` " + "instead if you really need to check for this low-level value.", + ) """Default value when the component state is not explicitly set.""" UNKNOWN = 1 diff --git a/tests/microgrid/electrical_components/proto/v1alpha8/test_state_code.py b/tests/microgrid/electrical_components/proto/v1alpha8/test_state_code.py index 65f0a04f..b87435fb 100644 --- a/tests/microgrid/electrical_components/proto/v1alpha8/test_state_code.py +++ b/tests/microgrid/electrical_components/proto/v1alpha8/test_state_code.py @@ -25,3 +25,4 @@ class TestElectricalComponentStateCodeParity(EnumParityTest): name_prefix = "ELECTRICAL_COMPONENT_STATE_CODE_" from_proto = staticmethod(electrical_component_state_code_from_proto) to_proto = staticmethod(electrical_component_state_code_to_proto) + deprecated_members = frozenset({"UNSPECIFIED"}) From 73411c0aa85c397dc67b7fd3bb63f113bf2d2d9e Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 26 Jun 2026 13:05:19 +0000 Subject: [PATCH 4/5] Deprecate `Event.UNSPECIFIED` Mark `Event.UNSPECIFIED` as deprecated. Signed-off-by: Leandro Lucarella --- src/frequenz/client/common/streaming/_event.py | 12 ++++++++---- tests/streaming/proto/v1alpha8/test_event.py | 1 + tests/streaming/test_event.py | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/frequenz/client/common/streaming/_event.py b/src/frequenz/client/common/streaming/_event.py index cac8f0c1..6907b476 100644 --- a/src/frequenz/client/common/streaming/_event.py +++ b/src/frequenz/client/common/streaming/_event.py @@ -3,14 +3,18 @@ """Streaming event type enum.""" -import enum +from frequenz.core.enum import Enum, deprecated_member, unique -@enum.unique -class Event(enum.Enum): +@unique +class Event(Enum): """A type of streaming event.""" - UNSPECIFIED = 0 + UNSPECIFIED = deprecated_member( + 0, + "Event.UNSPECIFIED is deprecated; use the `int` value `0` " + "instead if you really need to check for this low-level value.", + ) """Unspecified event type.""" CREATED = 1 diff --git a/tests/streaming/proto/v1alpha8/test_event.py b/tests/streaming/proto/v1alpha8/test_event.py index 8b8df90b..2fdf9bb0 100644 --- a/tests/streaming/proto/v1alpha8/test_event.py +++ b/tests/streaming/proto/v1alpha8/test_event.py @@ -21,3 +21,4 @@ class TestEventParity(EnumParityTest): name_prefix = "EVENT_" from_proto = staticmethod(event_from_proto) to_proto = staticmethod(event_to_proto) + deprecated_members = frozenset({"UNSPECIFIED"}) diff --git a/tests/streaming/test_event.py b/tests/streaming/test_event.py index 19acd9af..b4eb11e0 100644 --- a/tests/streaming/test_event.py +++ b/tests/streaming/test_event.py @@ -3,13 +3,16 @@ """Tests for the `Event` enum domain model.""" +import pytest + from frequenz.client.common.streaming import Event def test_event_members() -> None: """Test that Event has the expected members with correct values.""" assert [m.name for m in Event] == ["UNSPECIFIED", "CREATED", "UPDATED", "DELETED"] - assert Event.UNSPECIFIED.value == 0 + with pytest.warns(DeprecationWarning): + assert Event.UNSPECIFIED.value == 0 assert Event.CREATED.value == 1 assert Event.UPDATED.value == 2 assert Event.DELETED.value == 3 From ec35e7e892980e11f9be7439714f052c65db4541 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Fri, 26 Jun 2026 13:54:48 +0000 Subject: [PATCH 5/5] Update release notes Signed-off-by: Leandro Lucarella --- RELEASE_NOTES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 55b6f062..0cbddae7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -43,6 +43,9 @@ * `frequenz.client.common.grid.EnergyMarketCodeType` * `frequenz.client.common.metrics.Metric` * `frequenz.client.common.metrics.MetricConnectionCategory` + * `frequenz.client.common.microgrid.electrical_components.ElectricalComponentDiagnosticCode` + * `frequenz.client.common.microgrid.electrical_components.ElectricalComponentStateCode` + * `frequenz.client.common.streaming.Event` When loading these types from protobuf using dataclass-level converters (e.g., `delivery_area_from_proto`, `metric_sample_from_proto`), the low-level fields (`code_type`, `category`, `metric`) now store the raw integer `0` for unspecified values instead of the deprecated member. Unspecified values should be rare errors, so it is better to expose them only via the low-level interface.