Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fce3323
Add the User Guide index
llucax Aug 21, 2026
4e6087d
Add the typed IDs User Guide section
llucax Aug 21, 2026
9f21c2e
Add the safe accessors User Guide section
llucax Aug 21, 2026
b54349e
Add the numeric types User Guide section
llucax Aug 21, 2026
028a71a
Add the enum-or-int fields User Guide section
llucax Aug 21, 2026
fb2b0e6
Add the validity-in-the-type User Guide section
llucax Aug 21, 2026
355586e
Add the membership and bounds User Guide section
llucax Aug 21, 2026
c375e42
Add the reading string output User Guide section
llucax Aug 21, 2026
5dfcead
Add the wrapper overview User Guide section
llucax Aug 21, 2026
34b8252
Add the Wrapping Guide index
llucax Aug 21, 2026
53fc521
Add the wrapper package organization Wrapping Guide section
llucax Aug 21, 2026
4f64cfb
Add the enums Wrapping Guide section
llucax Aug 21, 2026
fd0cb2c
Add the data types Wrapping Guide section
llucax Aug 21, 2026
8b9cf6d
Add the validity Wrapping Guide section
llucax Aug 21, 2026
c27423e
Add the conversion functions Wrapping Guide section
llucax Aug 21, 2026
0632071
Add the deprecation and compatibility Wrapping Guide section
llucax Aug 21, 2026
40850d6
Add the testing Wrapping Guide section
llucax Aug 21, 2026
3ec2cc1
Point CONTRIBUTING.md at the Wrapping Guide
llucax Aug 21, 2026
d18cd0d
Add the Client Developer Guide index
llucax Aug 21, 2026
59f74dd
Add the namespace and versioning Client Developer Guide section
llucax Aug 21, 2026
07a70b1
Add the using conversion functions Client Developer Guide section
llucax Aug 21, 2026
8b83a61
Add the shipped converters Client Developer Guide section
llucax Aug 21, 2026
a62efcc
Add the building your own wrappers Client Developer Guide section
llucax Aug 21, 2026
65d0f19
Point the AGENTS.md files at the new guides
llucax Aug 21, 2026
5a96cba
Update release notes
llucax Aug 21, 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
9 changes: 6 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ types never leak into a public signature — they are confined to the `proto/` c
.
├── src/frequenz/client/common/ # the library (PEP 420 namespace pkg)
├── tests/ # mirrors src/ layout by domain + proto version
├── docs/{user-guide,client-developer-guide,wrapping-guide}/ # authored guides — the canonical how-to (see WHERE TO LOOK)
├── docs/_scripts/ # mkdocstrings autoapi generation (not prose)
├── noxfile.py # 8 lines; delegates everything to frequenz-repo-config
└── site/ # generated docs output — do NOT hand-edit or commit
Expand All @@ -27,8 +28,10 @@ types never leak into a public signature — they are confined to the `proto/` c

| Task | Location | Notes |
|------|----------|-------|
| Add/change a public type or enum | `src/.../<domain>/` | See `src/.../AGENTS.md` for the pattern |
| Add/change protobuf conversion | `src/.../<domain>/proto/<namespace>/` | Per API namespace; only `v1alpha8` exists |
| Design a wrapper or converter (patterns) | `docs/wrapping-guide/` | Canonical: enums, data types, validity, converters, deprecation. Don't duplicate it in code/AGENTS |
| Consume wrappers / build a client library | `docs/user-guide/`, `docs/client-developer-guide/` | Downstream users + client-lib authors |
| Add/change a public type or enum | `src/.../<domain>/` | Repo mechanics in `src/.../AGENTS.md`; design in `docs/wrapping-guide/` |
| Add/change protobuf conversion | `src/.../<domain>/proto/<namespace>/` | Per API namespace (only `v1alpha8`); see `docs/wrapping-guide/conversion-functions.md` |
| Shared enum proto helper | `src/.../proto/_enum.py` | `enum_from_proto` used by every enum wrapper |
| Reusable enum test scaffold | `src/.../test/enum_parity.py` | `EnumParityTest` base class |
| Add tests | `tests/<domain>/...` | Mirrors src tree; see `tests/AGENTS.md` |
Expand Down Expand Up @@ -57,7 +60,7 @@ types never leak into a public signature — they are confined to the `proto/` c
```bash
python -m pip install -e .[dev] # full dev install
nox # all checks (creates own venvs)
nox -R -s pytest -- tests/test_*.py # tests, reuse env
nox -R -s pytest_max -- tests/... # tests (min-deps: pytest_min); reuse env
nox -R -s pylint -- ... # lint; nox -R -s mypy -- ... for types
pytest # direct run (needs .[dev-pytest])
mkdocs serve # live docs preview
Expand Down
24 changes: 3 additions & 21 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,9 @@ GitHub pages produced for your fork.

## Wrapper conventions

### Field names and docstrings

The following rules apply when wrapping protobuf messages into idiomatic Python
types:

1. **Alignment**: By default, wrapper field names match the protobuf field names
unless a clear Pythonic improvement preserves or clarifies semantics.
2. **IDs**: Always keep the `_id` suffix for fields representing identifiers
(e.g., `id`, `microgrid_id`, `enterprise_id`, `source_id`, `destination_id`).
3. **Redundancy**: You may drop a redundant or long entity prefix while keeping
`_id`. For example, `source_electrical_component_id` becomes `source_id`.
4. **Time fields**: Use the `_time` suffix for both protobuf `_time` and
`_timestamp` fields. Never drop the suffix, as bare names like `start` or
`create` can be read as verbs or actions. For example, `create_timestamp`
becomes `create_time` and `start_timestamp` becomes `start_time`.
5. **Values**: You may drop the `_value` suffix inside a class ending in `Value`
when the remaining name remains clear. For example, `avg`, `min`, `max`, and
`raw` in `AggregatedMetricValue`.
6. **Docstrings**: Docstrings may be shorter or more Pythonic than the protobuf
comments, but they must not contradict, narrow, broaden, or operationally
reinterpret the protobuf semantics.
The [Wrapping Guide](https://frequenz-floss.github.io/frequenz-client-common-python/latest/wrapping-guide/)
documents the conventions for writing wrappers: package layout, type design,
conversion functions, and the field-name and docstring rules.

## Releasing

Expand Down
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@

* 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.

* Added three authored documentation guides, one per audience:

* **User Guide** — For users of the wrapper types: typed IDs, safe accessors and exceptions, numeric types, enum-or-int fields, validity in the type, membership and bounds, reading string output, and an overview of the available wrappers.
* **Client Developer Guide** — For `frequenz-client-*` library authors: which versioned `proto.v1alphaN` package to import, the usual client-method shapes, what this library already converts, and when to write your own wrappers.
* **Wrapping Guide** — For anyone designing a wrapper: package layout, enum representation, data types, validity in the type, conversion functions, deprecation, and testing. The field-name and docstring rules previously listed in `CONTRIBUTING.md` moved here.

## Bug Fixes

* Fixed `EnumParityTest` so protobuf values whose Python member name exists with a different number fail parity checks instead of being treated as unmirrored protobuf values.
Expand Down
3 changes: 3 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
* [Home](index.md)
* [User Guide](user-guide/)
* [Client Developer Guide](client-developer-guide/)
* [Wrapping Guide](wrapping-guide/)
* [API Reference](reference/)
* [Contributing](CONTRIBUTING.md)
5 changes: 5 additions & 0 deletions docs/client-developer-guide/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* [Overview](index.md)
* [Namespace and versioning](namespace-and-versioning.md)
* [Using conversion functions](using-conversion-functions.md)
* [Conversion functions provided by this library](shipped-converters.md)
* [Building your own wrappers](building-your-own.md)
41 changes: 41 additions & 0 deletions docs/client-developer-guide/building-your-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Building your own wrappers

This library provides high-level Python wrappers and conversion functions for
messages from
[`frequenz-api-common`](https://github.com/frequenz-floss/frequenz-api-common).
If your client receives protobuf messages from a service-specific API, write
the wrapper and its conversion functions in your own client library.

## Choose where to write the wrapper

Use the conversion functions this library provides for messages from
`frequenz-api-common`. They let every client return the same high-level Python
wrapper types.

Write a custom wrapper in your own client library for messages from a
service-specific API. Keep low-level protobuf types inside your conversion
functions. Your public methods should only expose high-level Python wrappers.

## Follow the Wrapping Guide

When you write custom wrappers and conversion functions, use the patterns in the
[Wrapping Guide](../wrapping-guide/index.md):

- [Organizing a wrapper package](../wrapping-guide/organizing-a-wrapper-package.md)
shows how to keep low-level protobuf imports out of wrapper modules.
- [Enums](../wrapping-guide/enums.md) and
[Data types](../wrapping-guide/data-types.md) show Python types for protobuf
fields and enums.
- [Validity in the type](../wrapping-guide/validity-in-the-type.md) and
[Conversion functions](../wrapping-guide/conversion-functions.md) show how to
represent invalid values, unknown enum numbers, and optional or `oneof` fields.
- [Deprecation and compatibility](../wrapping-guide/deprecation-and-compatibility.md)
and [Testing](../wrapping-guide/testing.md) show how to change wrapper APIs
safely and test conversion functions.

## Use shared wrappers in your custom conversion functions

When a service-specific protobuf message has a nested `frequenz-api-common`
message, call one of this library's conversion functions for that field. It
returns the shared high-level Python wrapper without making you write the same
translation again.
28 changes: 28 additions & 0 deletions docs/client-developer-guide/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Client Developer Guide

This guide is for developers building `frequenz-client-*` libraries with this
library. Your client gets low-level protobuf messages from a gRPC service. The
conversion functions this library provides turn them into high-level Python
wrappers that you return to your users.

If this library does not provide a wrapper you need, see the
[Wrapping Guide](../wrapping-guide/index.md). The [User Guide](../user-guide/index.md)
explains how users can work with the wrappers your client returns.

## Sections

- [Namespace and versioning](namespace-and-versioning.md) — Shows how the
conversion functions are grouped by protobuf API version. Import the group
that matches the messages your service sends.

- [Using conversion functions](using-conversion-functions.md) — Shows the two
usual client-method shapes: one protobuf message and a list of them. Your
methods convert the messages and return the wrappers.

- [Conversion functions provided by this library](shipped-converters.md) — Lists
the `v1alpha8` packages and the messages they translate. The table links each
package to its API reference.

- [Building your own wrappers](building-your-own.md) — Explains when your client
library needs its own wrapper types and conversion functions. It points to the
Wrapping Guide for the patterns to use.
52 changes: 52 additions & 0 deletions docs/client-developer-guide/namespace-and-versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Namespace and versioning

The conversion functions this library provides are grouped by the version of
the [`frequenz-api-common`](https://github.com/frequenz-floss/frequenz-api-common)
protobuf API they understand. Right now, this library provides `v1alpha8`.
Import the group that matches the messages your client receives.

## Import the matching version

When your client receives messages from the `v1alpha8` protobuf API, import
conversion functions from `frequenz.client.common.<domain>.proto.v1alpha8`.

For example,
[`location_from_proto`][frequenz.client.common.types.proto.v1alpha8.location_from_proto]
translates a protobuf
[`location_pb2.Location`][frequenz.api.common.v1alpha8.types.location_pb2.Location]
message into a high-level
[`Location`][frequenz.client.common.types.Location] wrapper:

```python
from frequenz.client.common.types.proto.v1alpha8 import location_from_proto

location = location_from_proto(response.location)
```

Your client works with the returned
[`Location`][frequenz.client.common.types.Location] wrapper.

## Keep your import and messages on the same version

The version of the protobuf messages determines which package you import. The
release version of this library does not.

- Import `frequenz.client.common.<domain>.proto.v1alpha8` for messages from the
`v1alpha8` protobuf API.
- Use `v1alphaN` only after your client uses that protobuf API version.

## When a new protobuf API version is available

When this library adds another protobuf API version, it adds a sibling package:

```text
types/
└── proto/
├── v1alpha8/
│ └── __init__.py
└── v1alphaN/
└── __init__.py
```

Clients that use `v1alpha8` keep importing `v1alpha8`. When your client moves
to a newer protobuf API version, update its imports to the matching package.
38 changes: 38 additions & 0 deletions docs/client-developer-guide/shipped-converters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Conversion functions provided by this library

The conversion functions this library provides translate the low-level protobuf
messages from
[`frequenz-api-common`](https://github.com/frequenz-floss/frequenz-api-common)
into high-level Python wrappers. This page lists the packages that contain them
and links to their API reference pages.

## v1alpha8 conversion packages

Right now, this library provides packages for the `v1alpha8` protobuf API.
Import the package that matches the messages your code receives.

| Package | What it translates |
| --- | --- |
| [`grid`][frequenz.client.common.grid.proto.v1alpha8] | Delivery areas ([`delivery_area_pb2.DeliveryArea`][frequenz.api.common.v1alpha8.grid.delivery_area_pb2.DeliveryArea]) through [`delivery_area_from_proto2`][frequenz.client.common.grid.proto.v1alpha8.delivery_area_from_proto2], plus energy-market code-type enum conversion. |
| [`metrics`][frequenz.client.common.metrics.proto.v1alpha8] | Metric samples ([`metrics_pb2.MetricSample`][frequenz.api.common.v1alpha8.metrics.metrics_pb2.MetricSample]), connections, aggregate values, bounds and bounds sets, plus metric and connection-category enum conversion. |
| [`microgrid`][frequenz.client.common.microgrid.proto.v1alpha8] | Microgrids ([`microgrid_pb2.Microgrid`][frequenz.api.common.v1alpha8.microgrid.microgrid_pb2.Microgrid]) and lifetimes through [`microgrid_from_proto`][frequenz.client.common.microgrid.proto.v1alpha8.microgrid_from_proto] and [`lifetime_from_proto`][frequenz.client.common.microgrid.proto.v1alpha8.lifetime_from_proto]. |
| [`microgrid.electrical_components`][frequenz.client.common.microgrid.electrical_components.proto.v1alpha8] | Electrical-component classes ([`electrical_components_pb2.ElectricalComponent`][frequenz.api.common.v1alpha8.microgrid.electrical_components.electrical_components_pb2.ElectricalComponent]), component instances and connections, plus category, diagnostic-code, and state-code enums. |
| [`pagination`][frequenz.client.common.pagination.proto.v1alpha8] | Pagination information ([`pagination_info_pb2.PaginationInfo`][frequenz.api.common.v1alpha8.pagination.pagination_info_pb2.PaginationInfo]) in both directions through [`pagination_info_from_proto`][frequenz.client.common.pagination.proto.v1alpha8.pagination_info_from_proto] and [`pagination_info_to_proto`][frequenz.client.common.pagination.proto.v1alpha8.pagination_info_to_proto]. |
| [`streaming`][frequenz.client.common.streaming.proto.v1alpha8] | Streaming events ([`event_pb2.Event`][frequenz.api.common.v1alpha8.streaming.event_pb2.Event]) in both directions through [`event_from_proto`][frequenz.client.common.streaming.proto.v1alpha8.event_from_proto] and [`event_to_proto`][frequenz.client.common.streaming.proto.v1alpha8.event_to_proto]. |
| [`types`][frequenz.client.common.types.proto.v1alpha8] | Locations ([`location_pb2.Location`][frequenz.api.common.v1alpha8.types.location_pb2.Location]) through [`location_from_proto`][frequenz.client.common.types.proto.v1alpha8.location_from_proto]. |

## Choose a conversion direction

Call a `*_from_proto` function after your code receives a low-level protobuf
message over gRPC. It returns a high-level Python wrapper. Call a `*_to_proto`
function when your code builds a low-level protobuf message to send over gRPC.
Some packages only provide one direction because their protobuf API only needs
one direction.

Import from the public package in the table, such as
`frequenz.client.common.types.proto.v1alpha8`. Do not import from an internal
module whose name starts with an underscore.

Use the API reference links in the table for parameters, return types, and the
full function lists. For the usual client-method pattern, see
[Using conversion functions](using-conversion-functions.md).
27 changes: 27 additions & 0 deletions docs/client-developer-guide/using-conversion-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Using conversion functions

Your client library calls a gRPC service and gets back low-level protobuf
messages. The conversion functions this library provides translate those into
the high-level Python wrappers, so the rest of your code — and your users —
work with wrappers instead of protobuf.

## Convert a single message

```python
response = await self._stub.GetMetricSample(request)
return metric_sample_from_proto(response.metric_sample)
```

## Convert a list of messages

```python
response = await self._stub.ListMetricSamples(request)
return [metric_sample_from_proto(s) for s in response.samples]
```

## You don't handle unknown or invalid values here

The conversion function already deals with them: it keeps an enum value your
version doesn't recognize as a plain `int`, and returns an `Invalid*` wrapper
instead of raising when data is malformed. You just return the wrapper.
Deciding what those cases *mean* is up to your users — see the [User Guide](../user-guide/index.md).
9 changes: 9 additions & 0 deletions docs/user-guide/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
* [Overview](index.md)
* [Typed IDs](typed-ids.md)
* [Safe accessors & exceptions](safe-accessors.md)
* [Numeric types](numeric-types.md)
* [Enum-or-int fields](enum-or-int-fields.md)
* [Validity in the type](validity-in-the-type.md)
* [Membership & bounds](membership-and-bounds.md)
* [Reading string output](reading-string-output.md)
* [Overview of available wrappers](overview.md)
Loading