Skip to content

Add the new Pipe and deprecate the experimental one - #589

Closed
llucax wants to merge 2 commits into
frequenz-floss:v1.x.xfrom
llucax:new-pipe
Closed

llucax wants to merge 2 commits into
frequenz-floss:v1.x.xfrom
llucax:new-pipe

Conversation

@llucax

@llucax llucax commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Add the Pipe from #565 as frequenz.channels.Pipe, so projects can switch to its closing behavior while they still support both v1 and v2, and deprecate frequenz.channels.experimental.Pipe, to remove it in v2.0.0 (#365).

The two are separate implementations, not an alias, because they behave differently: the new Pipe closes its sender when its receiver stops, and closes its sender and its receiver when sending fails with a SenderError (unless close_pipe_on_send_error=False, which drops the message and keeps going). The experimental one leaves both ends open. The deprecation warning, the docstring and the release notes explain this, so users can check their teardown before switching.

NoReceiversError only exists on v2.x.x, so here every SenderError is logged as an error; v2 only uses it to pick a quieter log level.

Fixes #583

The experimental `Pipe` leaves its sender and its receiver open when it
stops by itself, so every user has to work out how to close them, and a
failed send ends its task with an exception nobody reads. Fixing that in
place would change the behavior of an existing class under users that
may rely on it, so add the new behavior as a separate `Pipe` at the top
level, and let users move to it when they have checked how they close
their endpoints.

The new `Pipe` closes its sender when its receiver stops, and closes
both its sender and its receiver when sending fails with a
`SenderError`, logging the failure. With `close_pipe_on_send_error=False`
it drops the message that failed and keeps forwarding the next ones.
`stop()` and any other exception leave both ends open.

This is the `Pipe` from frequenz-floss#565, which changed the experimental one in
place on `v2.x.x`. It doesn't use `NoReceiversError`, which only exists
on `v2.x.x`: it only picked the log level there, so here every
`SenderError` is logged as an error with its traceback. The tests are
ported accordingly, as `BroadcastChannel` in v1 closes itself when it is
sent a message without receivers, instead of staying open.

The experimental `Pipe` is deprecated in the next commit.

Signed-off-by: Leandro Lucarella <luca-frequenz@llucax.com>
The new `Pipe` replaces the experimental one, but it isn't a drop-in
replacement: it closes its sender when its receiver stops, and both ends
when sending fails, while the experimental one leaves them open. Users
should check how they tear their pipes down before they switch, so the
warning and the docstring explain the differences, not only the new
name.

Keep the experimental `Pipe` as a separate implementation with
`@deprecated` instead of making it an alias of the new one, as the two
don't behave the same and an alias would change the behavior of existing
code silently. The `Deprecated:` notice is kept to a pointer to the
replacement and what to check, and the full list of differences goes in
the docstring body, as the deprecations guide asks.

The existing tests keep checking the behavior of the experimental `Pipe`
and now assert that it warns, and a few more document how it leaves its
ends open.

The experimental `Pipe` is removed in v2.0.0.

Signed-off-by: Leandro Lucarella <luca-frequenz@llucax.com>
@github-actions github-actions Bot added part:docs Affects the documentation part:tests Affects the unit, integration and performance (benchmarks) tests part:experimental Affects the experimental package labels Nov 5, 2026
@llucax
llucax requested a balanced review from Copilot November 5, 2026 14:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block approval of the stable Pipe API and experimental migration changes.

No open issues 🎉

What changed in this PR

Adds a stable frequenz.channels.Pipe with explicit endpoint-closing behavior, allowing projects to migrate before v2 while preserving the experimental implementation’s behavior.

Changes:

  • Exports the new Pipe, with automatic endpoint closure and an option to continue after send failures.
  • Deprecates the experimental Pipe and documents migration and endpoint ownership.
  • Adds tests for stable behavior, error handling, deprecation warnings, and experimental compatibility.
File Description
tests/​test_pipe.py Tests forwarding, endpoint closure, error handling, and warning-free use.
tests/​experimental/​test_pipe.py Verifies deprecation warnings and preserved legacy behavior.
src/​frequenz/​channels/​experimental/​_pipe.py Adds deprecation warnings and migration guidance.
src/​frequenz/​channels/​_pipe.py Implements the stable pipe and documents endpoint ownership.
src/​frequenz/​channels/​__init__.py Exports the stable Pipe publicly.
RELEASE_NOTES.md Announces the new API and experimental deprecation.
docs/​user-guide/​utilities/​pipes.md Adds a user-guide page for the stable pipe.

Review effort: Balanced

@llucax

llucax commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

I’m closing this PR and deferring the new top-level Pipe to v2.0.0. The experimental Pipe will stay unchanged and undeprecated in v1.

The goal was to let users adopt the new Pipe in v1.13.0, then upgrade to v2.0.0 without another behavior change. But Pipe depends on channel behavior that differs between these versions.

When a BroadcastChannel has no receivers, a send closes the channel in v1. In v2, the send raises NoReceiversError but leaves the channel open. This affects Pipe:

  • With close_pipe_on_send_error=False, the v1 pipe keeps running, but every later send fails because the channel is closed. In v2, it can send again when a new receiver subscribes.
  • With the default setting, the v2 pipe closes only its own sender, so other senders can remain usable. In v1, the failed send has already closed the whole channel.

Adding NoReceiversError to v1 wouldn’t fix this, because the channel would still close. Introducing the new Pipe only in v2 makes the behavior change part of the v2 migration, instead of asking users to migrate in v1 and then face another change in v2.

This replaces the plan in #583 to introduce the new Pipe in v1.13.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

part:docs Affects the documentation part:experimental Affects the experimental package part:tests Affects the unit, integration and performance (benchmarks) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the new Pipe to frequenz.channels in v2 and deprecate the experimental one

2 participants