Skip to content

Commit 7bdac82

Browse files
authored
Add metrics package compatible with v1alpha8 (#128)
This PR introduces a new `metrics` package compatible with the v1alpha8 API version, along with a new `proto` package for common protobuf conversion utilities. The changes deprecate the old `enum_proto` module in favor of the new `proto.enum_from_proto` function and improve deprecation messages across the codebase to include full module paths. **Key changes:** - Adds new `frequenz.client.common.proto` package with `enum_from_proto`, `datetime_from_proto`, and `datetime_to_proto` utilities - Introduces `frequenz.client.common.metrics` package with comprehensive metric types (`Metric`, `MetricSample`, `AggregatedMetricValue`, `Bounds`, etc.) compatible with v1alpha8 - Deprecates `frequenz.client.common.enum_proto` module with improved deprecation messages that include full module paths - Updates all existing deprecation messages to include full module paths for better clarity
2 parents f2bc0fe + 9121ffc commit 7bdac82

32 files changed

+2017
-55
lines changed

RELEASE_NOTES.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@
22

33
## Summary
44

5-
This is the same release as v0.3.5 but with prefixes in `Event` enum values removed. The v0.3.5 release will be yanked from PyPI and it should not be used.
5+
This release main change is the introduction of a new `metrics` package compatible with the common API v0.8 (`v1alpha8`) (the old `metric` package, in singular, still works with the old v0.5/`v1` version).
66

7-
## Upgrading
7+
## Deprecations
88

9-
- The `pagination.Params` class is deprecated; use the protobuf message directly.
10-
- The `pagination.Info` class is deprecated in favor of the new `pagination.PaginationInfo` class.
9+
- The old `frequenz.client.common.enum_proto` module is now deprecated, please use `frequenz.client.common.proto.enum_from_proto` instead.
1110

1211
## New Features
1312

14-
- Mapping for the new `Event` message has been added.
15-
- Add new common API enums for `ElectricalComponent` (previously `Components`).
16-
17-
- Added `v1alpha8` variants of the pagination data structures.
18-
19-
## Bug Fixes
20-
21-
- Updated display of protobuf version warnings
13+
- New `frequenz.client.common.common.proto` module with conversion utilities for protobuf types:
14+
- `enum_from_proto()` (moved from `enum_proto).
15+
- `datetime_to_proto()` and `datetime_from_proto()` functions to convert between Python `datetime` and protobuf `Timestamp` (imported from `frequenz-client-base`.
16+
- New `metrics` package compatible with API v0.8, which includes:
17+
- `Metric` enum with all supported metrics.
18+
- `MetricSample` dataclass to represent metric samples.
19+
- `AggregatedMetricValue` dataclass to represent derived statistical summaries.
20+
- `Bounds` dataclass to represent bounds for metrics.
21+
- `MetricConnection` and `MetricConnectionCategory` to represent connections from which metrics are obtained.
22+
- `proto` submodule with conversion functions to/from protobuf types.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies = [
2929
"typing-extensions >= 4.13.0, < 5",
3030
"frequenz-api-common >= 0.8.0, < 1",
3131
"frequenz-core >= 1.0.2, < 2",
32+
"protobuf >= 6.33.1, < 7",
3233
]
3334
dynamic = ["version"]
3435

@@ -60,6 +61,7 @@ dev-mkdocs = [
6061
dev-mypy = [
6162
"mypy == 1.18.2",
6263
"types-Markdown == 3.9.0.20250906",
64+
"types-protobuf == 6.32.1.20251105",
6365
# For checking the noxfile, docs/ script, and tests
6466
"frequenz-client-common[dev-mkdocs,dev-noxfile,dev-pytest]",
6567
]
@@ -72,6 +74,7 @@ dev-pylint = [
7274
dev-pytest = [
7375
"pytest == 8.4.2",
7476
"frequenz-repo-config[extra-lint-examples] == 0.13.6",
77+
"hypothesis == 6.140.3",
7578
"pytest-mock == 3.15.1",
7679
"pytest-asyncio == 1.2.0",
7780
"async-solipsism == 0.8",

src/frequenz/client/common/enum_proto.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import enum
77
from typing import Literal, TypeVar, overload
88

9+
from typing_extensions import deprecated
10+
911
EnumT = TypeVar("EnumT", bound=enum.Enum)
1012
"""A type variable that is bound to an enum."""
1113

@@ -22,6 +24,10 @@ def enum_from_proto(
2224
) -> EnumT | int: ...
2325

2426

27+
@deprecated(
28+
"frequenz.client.common.enum_proto.enum_from_proto is deprecated. "
29+
"Please use frequenz.client.common.proto.enum_from_proto instead."
30+
)
2531
def enum_from_proto(
2632
value: int, enum_type: type[EnumT], *, allow_invalid: bool = True
2733
) -> EnumT | int:

src/frequenz/client/common/metric/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ class Metric(enum.Enum):
142142
SENSOR_IRRADIANCE = PBMetric.METRIC_SENSOR_IRRADIANCE
143143

144144
@classmethod
145-
@deprecated("Use `frequenz.client.common.enum_proto.enum_from_proto` instead.")
145+
@deprecated(
146+
"frequenz.client.common.metric.Metric.from_proto() is deprecated. "
147+
"Use frequenz.client.common.proto.enum_from_proto instead."
148+
)
146149
def from_proto(cls, metric: PBMetric.ValueType) -> Self:
147150
"""Convert a protobuf Metric value to Metric enum.
148151
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Metrics definitions."""
5+
6+
from ._bounds import Bounds
7+
from ._metric import Metric
8+
from ._sample import (
9+
AggregatedMetricValue,
10+
AggregationMethod,
11+
MetricConnection,
12+
MetricConnectionCategory,
13+
MetricSample,
14+
)
15+
16+
__all__ = [
17+
"AggregatedMetricValue",
18+
"AggregationMethod",
19+
"Bounds",
20+
"Metric",
21+
"MetricConnection",
22+
"MetricConnectionCategory",
23+
"MetricSample",
24+
]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# License: MIT
2+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3+
4+
5+
"""Definitions for bounds."""
6+
7+
import dataclasses
8+
9+
10+
@dataclasses.dataclass(frozen=True, kw_only=True)
11+
class Bounds:
12+
"""A set of lower and upper bounds for any metric.
13+
14+
The lower bound must be less than or equal to the upper bound.
15+
16+
The units of the bounds are always the same as the related metric.
17+
"""
18+
19+
lower: float | None = None
20+
"""The lower bound.
21+
22+
If `None`, there is no lower bound.
23+
"""
24+
25+
upper: float | None = None
26+
"""The upper bound.
27+
28+
If `None`, there is no upper bound.
29+
"""
30+
31+
def __post_init__(self) -> None:
32+
"""Validate these bounds."""
33+
if self.lower is None:
34+
return
35+
if self.upper is None:
36+
return
37+
if self.lower > self.upper:
38+
raise ValueError(
39+
f"Lower bound ({self.lower}) must be less than or equal to upper "
40+
f"bound ({self.upper})"
41+
)
42+
43+
def __str__(self) -> str:
44+
"""Return a string representation of these bounds."""
45+
return f"[{self.lower}, {self.upper}]"

0 commit comments

Comments
 (0)