Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
55a9efe
Stop logging issues in `microgrid_from_proto()`
llucax Jul 13, 2026
958c879
Add `BaseBounds` abstract base class
llucax Jul 16, 2026
ac9009e
Make `Bounds` inherit from `BaseBounds`
llucax Jul 16, 2026
61b8c02
Use `[lower,upper]` format in `Bounds.__str__`
llucax Jul 16, 2026
a36f73b
Add `InvalidBounds`
llucax Jul 16, 2026
09f3080
Add `MissingBounds`
llucax Jul 16, 2026
7bf61ff
Add `InvalidBoundsError`
llucax Jul 16, 2026
9a8c3f1
Add `bounds_from_proto2` conversion function
llucax Jul 16, 2026
04d1529
Deprecate `bounds_from_proto`
llucax Jul 16, 2026
83db357
Deprecate `bounds_from_proto_with_issues`
llucax Jul 16, 2026
ea8f4f7
Update `metric_config_bounds` to preserve invalid bounds
llucax Jul 16, 2026
27a3346
Add safe accessors for metric config bounds
llucax Jul 16, 2026
3212350
Split `Bounds` tests into per-type files
llucax Jul 16, 2026
a2fc349
Remove `MissingBounds`
llucax Jul 17, 2026
3caa1fd
Make `get_metric_config_bounds()` mimic `dict.get()`
llucax Jul 17, 2026
86536ee
Update release notes
llucax Jul 16, 2026
ea1c87a
Add `__contains__` to `Bounds`
llucax Jul 19, 2026
e027efb
Add `__bool__` and `is_bounded()` to `Bounds`
llucax Jul 19, 2026
8773d97
Add `BoundsSet`
llucax Jul 19, 2026
025b5a3
Add `InvalidBoundsSet`
llucax Jul 19, 2026
15ec14f
Migrate `MetricSample.bounds` to `bounds_set`
llucax Jul 19, 2026
3d182a0
Add `InvalidBoundsSetError`
llucax Jul 19, 2026
a9b4dba
Add safe accessor `MetricSample.get_bounds_set()`
llucax Jul 19, 2026
49850be
Reject `NaN` in bounds membership tests
llucax Jul 19, 2026
bde32b6
Normalize the deprecated `MetricSample.bounds` property
llucax Jul 19, 2026
141ab23
Update release notes
llucax Jul 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@

A well-formed `DeliveryArea` has a non-empty `code` and a specified (non-`UNSPECIFIED`) `code_type`. Constructing one with invalid data currently emits a `DeprecationWarning`; a future release will replace the warning with a hard `ValueError`. To opt into the upcoming behavior right now, pass `_raise_on_invalid=True` to the constructor. Prefer `delivery_area_from_proto2` to load delivery areas from the wire — malformed messages become `InvalidDeliveryArea` instances instead.

* `frequenz.client.common.metrics.proto.v1alpha8.bounds_from_proto` is now deprecated; use `bounds_from_proto2` instead.

The new converter returns `Bounds | InvalidBounds` and surfaces malformed wire data at the type level rather than raising a `ValueError` when `lower > upper`. The old converter continues to work but emits a `DeprecationWarning`.

* `frequenz.client.common.metrics.proto.v1alpha8.bounds_from_proto_with_issues` is now deprecated with no direct replacement.

Validity is now encoded in the return type of `bounds_from_proto2` (`Bounds | InvalidBounds`), so callers should inspect the returned type instead of collecting issue strings via a side channel. The old converter continues to work but emits a `DeprecationWarning`.

* `frequenz.client.common.metrics.Bounds.__str__` now renders as `[lower,upper]` (no space after the comma) to match the compact format used by `Lifetime` and to compose cleanly with the `<invalid:...>` marker on `InvalidBounds`.

* `frequenz.client.common.metrics.MetricSample.bounds` is now deprecated; use `bounds_set` instead.

The field type changed from `list[Bounds]` to `BoundsSet | InvalidBoundsSet` (see New Features). Reads and construction remain backward compatible: passing the `bounds=` keyword argument still works (it builds a `BoundsSet` and emits a `DeprecationWarning`), and reading `MetricSample.bounds` still returns the valid `Bounds` as a `list` (also emitting a `DeprecationWarning`). The compatibility property returns only the valid, normalized bounds, so it may differ from the raw wire list when bounds overlapped or touched.

* `frequenz.client.common.metrics.proto.v1alpha8.metric_sample_from_proto_with_issues` no longer drops invalid bounds or reports them as a major issue.

Malformed bounds are now preserved in the returned `MetricSample.bounds_set` as an `InvalidBoundsSet` (validity is encoded in the type), so the previous "bounds for ... is invalid, ignoring these bounds" major issue is no longer produced.

## New Features

* Added 4 new electrical component classes for categories that previously collapsed into `UnrecognizedElectricalComponent`:
Expand Down Expand Up @@ -81,6 +99,8 @@
* `frequenz.client.common.grid.DeliveryArea.get_code_type()`
* `frequenz.client.common.metrics.MetricConnection.get_category()`
* `frequenz.client.common.metrics.MetricSample.get_metric()`
* `frequenz.client.common.metrics.MetricSample.get_bounds_set()`
* `frequenz.client.common.microgrid.electrical_components.ElectricalComponent.get_metric_config_bounds()`

* Added new delivery-area class hierarchy:

Expand All @@ -92,6 +112,20 @@

* Added a new `frequenz.client.common.microgrid.Lifetime` type together with the `frequenz.client.common.microgrid.proto.v1alpha8.lifetime_from_proto` conversion function.

* Added `frequenz.client.common.metrics.proto.v1alpha8.bounds_from_proto2` returning `Bounds | InvalidBounds`. This is the replacement for the now-deprecated `bounds_from_proto`.

* `frequenz.client.common.metrics.Bounds` gained containment check capabilities:

* `value in bounds` (`__contains__`) tests membership, inclusive on both ends, with a `None` bound meaning unbounded in that direction.
* `bool(bounds)` and `bounds.is_bounded()` report whether the bounds restrict anything; a fully unbounded `Bounds()` is falsy.

* Added a new bounds-set class hierarchy:

* `frequenz.client.common.metrics.BoundsSet` — a normalized union of `Bounds` with an efficient `value in bounds_set` membership test. Overlapping and touching bounds are merged on construction, and the empty set is the unbounded set (it contains every value and is falsy).
* `frequenz.client.common.metrics.InvalidBoundsSet` — a set built from bounds that included at least one `InvalidBounds`; it preserves all the raw bounds unmerged and provides no membership test.

* Added a new `frequenz.client.common.metrics.MetricSample.bounds_set` field, typed `BoundsSet | InvalidBoundsSet`, replacing the deprecated `bounds` list (see Upgrading). Malformed wire bounds are preserved as an `InvalidBoundsSet` instead of being dropped. Use `get_bounds_set()` to resolve it to a valid `BoundsSet` or a clear `InvalidBoundsSetError`.

* Added a new `frequenz.client.common.types.Location` type together with the `frequenz.client.common.types.proto.v1alpha8.location_from_proto` conversion function.

* Added a new `frequenz.client.common.microgrid.Microgrid` type, together with the `frequenz.client.common.microgrid.proto.v1alpha8.microgrid_from_proto` conversion function.
Expand All @@ -100,6 +134,8 @@

The class of a component is its identity; components don't carry category or type attributes. The only exceptions are the error-recovery classes `UnrecognizedElectricalComponent` and `MismatchedCategoryElectricalComponent` (with a raw protobuf `category` value) and `UnrecognizedBattery`, `UnrecognizedInverter` and `UnrecognizedEvCharger` (with a raw protobuf `type` value), which preserve the raw protobuf values received from the protocol version used to load them.

`ElectricalComponent.metric_config_bounds` is typed `Mapping[Metric | int, Bounds | InvalidBounds]`: malformed wire entries are preserved as `InvalidBounds` instead of being silently dropped, and entries that named a metric but carried no `config_bounds` field load as an unbounded `Bounds()` (that is the explicit wire encoding of an unbounded metric, since protobuf never serializes an all-defaults `Bounds` message). Use `get_metric_config_bounds()` to resolve an entry to a valid `Bounds` or a clear `InvalidBoundsError`; it mimics `dict.get()`, returning an unbounded `Bounds()` (or a caller-supplied `default`) for absent metrics.

* Added a new `frequenz.client.common.microgrid.Microgrid` type with a raising `is_active()` method, together with the `frequenz.client.common.microgrid.proto.v1alpha8.microgrid_from_proto` conversion function.

## Bug Fixes
Expand Down
16 changes: 15 additions & 1 deletion src/frequenz/client/common/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@

"""Metrics definitions."""

from ._bounds import Bounds
from ._bounds import (
BaseBounds,
Bounds,
BoundsSet,
InvalidBounds,
InvalidBoundsError,
InvalidBoundsSet,
InvalidBoundsSetError,
)
from ._metric import Metric
from ._sample import (
AggregatedMetricValue,
Expand All @@ -16,7 +24,13 @@
__all__ = [
"AggregatedMetricValue",
"AggregationMethod",
"BaseBounds",
"Bounds",
"BoundsSet",
"InvalidBounds",
"InvalidBoundsError",
"InvalidBoundsSet",
"InvalidBoundsSetError",
"Metric",
"MetricConnection",
"MetricConnectionCategory",
Expand Down
Loading
Loading