Skip to content

Commit 39ade4e

Browse files
authored
Revamp modules structure (#235)
- Remove `Bidirectional` channel - Remove `Peekable` and associated methods/classes - Don't import `TypeVar`s from other modules - Make all `TypeVar`s private - Split `_base_classes` into `_receiver` and `_sender` - Make channels return the base sender and receiver types - Make channel's sender and receiver implementation private - Don't use aliases for the base sender and receiver classes - Move merge and select symbols to the top level - Move utility receivers to their own public modules - Move nested class out of the parent class Fixes #233, fixes #234.
2 parents 4a7f1e6 + cc17298 commit 39ade4e

27 files changed

+386
-832
lines changed

.github/ISSUE_TEMPLATE/bug.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ body:
5050
- Documentation (part:docs)
5151
- Unit, integration and performance tests (part:tests)
5252
- Build script, CI, dependencies, etc. (part:tooling)
53-
- Channels, `Broadcast`, `Bidirectional`, etc. (part:channels)
53+
- Channels, `Broadcast`, `Anycast`, etc. (part:channels)
5454
- Select (part:select)
5555
- Utility receivers, `Merge`, etc. (part:receivers)
5656
validations:

.github/labeler.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# For more details on the configuration please see:
77
# https://github.com/marketplace/actions/labeler
88

9-
"part:docs":
9+
"part:docs":
1010
- "**/*.md"
1111
- "docs/**"
1212
- "examples/**"
@@ -31,14 +31,15 @@
3131
- noxfile.py
3232

3333
"part:channels":
34-
- any:
35-
- "src/frequenz/channels/**"
36-
- "!src/frequenz/channels/util/**"
34+
- "src/frequenz/channels/_anycast.py"
35+
- "src/frequenz/channels/_broadcast.py"
3736

3837
"part:receivers":
39-
- any:
40-
- "src/frequenz/channels/util/**"
41-
- "!src/frequenz/channels/util/_select.py"
38+
- "src/frequenz/channels/_merge.py"
39+
- "src/frequenz/channels/_merge_named.py"
40+
- "src/frequenz/channels/event.py"
41+
- "src/frequenz/channels/file_watcher.py"
42+
- "src/frequenz/channels/timer.py"
4243

4344
"part:select":
44-
- "src/frequenz/channels/util/_select.py"
45+
- "src/frequenz/channels/_select.py"

RELEASE_NOTES.md

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
## Summary
44

5-
The `Timer` now can be started with a delay.
65

76
## Upgrading
87

@@ -12,11 +11,9 @@ The `Timer` now can be started with a delay.
1211

1312
You should instantiate using `Anycast(name=..., limit=...)` (or `Anycast(name=...)` if the default `limit` is enough) instead of `Anycast(...)` or `Anycast(maxsize=...)`.
1413

15-
* `Bidirectional`
16-
17-
- The `client_id` and `service_id` arguments were merged into a keyword-only `name`.
14+
- `new_sender` and `new_receiver`: They now return a base `Sender` and `Receiver` class (respectively) instead of a channel-specific `Sender` or `Receiver` subclass.
1815

19-
You should instantiate using `Bidirectional(name=...)` instead of `Bidirectional(..., ...)` or `Bidirectional(client_id=..., service_id=...)`.
16+
This means users now don't have access to the internals to the channel-specific `Sender` and `Receiver` subclasses.
2017

2118
* `Broadcast`
2219

@@ -28,16 +25,71 @@ The `Timer` now can be started with a delay.
2825

2926
You should use `.new_receiver(name=name, limit=limit)` (or `.new_receiver()` if the defaults are enough) instead of `.new_receiver(name)` or `.new_receiver(name, maxsize)`.
3027

28+
- `new_sender` and `new_receiver` now return a base `Sender` and `Receiver` class (respectively) instead of a channel-specific `Sender` or `Receiver` subclass.
29+
30+
This means users now don't have access to the internals to the channel-specific `Sender` and `Receiver` subclasses.
31+
3132
* `Event`
3233

3334
- `__init__`: The `name` argument was made keyword-only. The default was changed to a more readable version of `id(self)`.
3435

3536
You should instantiate using `Event(name=...)` instead of `Event(...)`.
3637

38+
- Moved from `frequenz.channels.util` to `frequenz.channels.event`.
39+
40+
* `FileWatcher`
41+
42+
- Moved from `frequenz.channels.util` to `frequenz.channels.file_watcher`.
43+
44+
- Support classes are no longer nested inside `FileWatcher`. They are now top-level classes within the new `frequenz.channels.file_watcher` module (e.g., `frequenz.channels.util.FileWatcher.EventType` -> `frequenz.channels.file_watcher.EventType`, `frequenz.channels.util.FileWatcher.Event` -> `frequenz.channels.file_watcher.Event`).
45+
46+
* `Timer` and support classes
47+
48+
- Moved from `frequenz.channels.util` to `frequenz.channels.timer`.
49+
3750
* All exceptions that took `Any` as the `message` argument now take `str` instead.
3851

3952
If you were passing a non-`str` value to an exception, you should convert it using `str(value)` before passing it to the exception.
4053

54+
* The following symbols were moved to the top-level `frequenz.channels` package:
55+
56+
- `Merge`
57+
- `MergeNamed`
58+
- `Selected`
59+
- `SelectError`
60+
- `SelectErrorGroup`
61+
- `UnhandledSelectedError`
62+
- `select`
63+
- `selected_from`
64+
65+
### Removals
66+
67+
* `Bidirectional`
68+
69+
This channel was removed as it is not recommended practice and was a niche use case. If you need to use it, you can set up two channels or copy the `Bidirectional` class from the previous version to your project.
70+
71+
* `Peekable`
72+
73+
This class was removed because it was merely a shortcut to a receiver that caches the last value received. It did not fit the channel abstraction well and was infrequently used.
74+
75+
You can replace it with a task that receives and retains the last value.
76+
77+
* `Broadcast.new_peekable()`
78+
79+
This was removed alongside `Peekable`.
80+
81+
* `Receiver.into_peekable()`
82+
83+
This was removed alongside `Peekable`.
84+
85+
* `ReceiverInvalidatedError`
86+
87+
This was removed alongside `Peekable` (it was only raised when using a `Receiver` that was converted into a `Peekable`).
88+
89+
* `util`
90+
91+
The entire `util` package was removed and its symbols were either moved to the top-level package or to their own public modules (as noted above).
92+
4193
## New Features
4294

4395
* `Anycast`

src/frequenz/channels/__init__.py

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,32 @@
66
This package contains
77
[channel](https://en.wikipedia.org/wiki/Channel_(programming)) implementations.
88
9+
Base classes:
10+
11+
* [Receiver][frequenz.channels.Receiver]: An object that can wait for and
12+
consume messages from a channel.
13+
14+
* [Sender][frequenz.channels.Sender]: An object that can send messages to
15+
a channel.
16+
917
Channels:
1018
1119
* [Anycast][frequenz.channels.Anycast]: A channel that supports multiple
1220
senders and multiple receivers. A message sent through a sender will be
1321
received by exactly one receiver.
1422
15-
* [Bidirectional][frequenz.channels.Bidirectional]: A channel providing
16-
a `client` and a `service` handle to send and receive bidirectionally.
17-
1823
* [Broadcast][frequenz.channels.Broadcast]: A channel to broadcast messages
1924
from multiple senders to multiple receivers. Each message sent through any of
2025
the senders is received by all of the receivers.
2126
22-
Other base classes:
27+
Utilities to work with channels:
2328
24-
* [Peekable][frequenz.channels.Peekable]: An object to allow users to get
25-
a peek at the latest value in the channel, without consuming anything.
29+
* [Merge][frequenz.channels.Merge] and [MergeNamed][frequenz.channels.MergeNamed]:
30+
[Receivers][frequenz.channels.Receiver] that merge messages coming from multiple
31+
receivers into a single stream.
2632
27-
* [Receiver][frequenz.channels.Receiver]: An object that can wait for and
28-
consume messages from a channel.
29-
30-
* [Sender][frequenz.channels.Sender]: An object that can send messages to
31-
a channel.
32-
33-
Utilities:
34-
35-
* [util][frequenz.channels.util]: A module with utilities, like special
36-
receivers that implement timers, file watchers, merge receivers, or wait for
37-
messages in multiple channels.
33+
* [select][frequenz.channels.select]: Iterate over the values of all
34+
[receivers][frequenz.channels.Receiver] as new values become available.
3835
3936
Exception classes:
4037
@@ -56,39 +53,61 @@
5653
* [ReceiverStoppedError][frequenz.channels.ReceiverStoppedError]: A receiver
5754
stopped producing messages.
5855
59-
* [ReceiverInvalidatedError][frequenz.channels.ReceiverInvalidatedError]:
60-
A receiver is not longer valid (for example if it was converted into
61-
a peekable.
56+
* [SelectError][frequenz.channels.SelectError]: Base class for all errors
57+
related to [select][frequenz.channels.select].
58+
59+
* [SelectErrorGroup][frequenz.channels.SelectErrorGroup]: A group of errors
60+
raised by [select][frequenz.channels.select].
61+
62+
* [UnhandledSelectedError][frequenz.channels.UnhandledSelectedError]: An error
63+
raised by [select][frequenz.channels.select] that was not handled by the
64+
user.
65+
66+
Extra utility receivers:
67+
68+
* [Event][frequenz.channels.event.Event]: A receiver that generates a message when
69+
an event is set.
70+
71+
* [FileWatcher][frequenz.channels.file_watcher.FileWatcher]: A receiver that
72+
generates a message when a file is added, modified or deleted.
73+
74+
* [Timer][frequenz.channels.timer.Timer]: A receiver that generates a message after a
75+
given amount of time.
6276
"""
6377

64-
from . import util
6578
from ._anycast import Anycast
66-
from ._base_classes import Peekable, Receiver, Sender
67-
from ._bidirectional import Bidirectional
6879
from ._broadcast import Broadcast
69-
from ._exceptions import (
70-
ChannelClosedError,
71-
ChannelError,
72-
Error,
73-
ReceiverError,
74-
ReceiverInvalidatedError,
75-
ReceiverStoppedError,
76-
SenderError,
80+
from ._exceptions import ChannelClosedError, ChannelError, Error
81+
from ._merge import Merge
82+
from ._merge_named import MergeNamed
83+
from ._receiver import Receiver, ReceiverError, ReceiverStoppedError
84+
from ._select import (
85+
Selected,
86+
SelectError,
87+
SelectErrorGroup,
88+
UnhandledSelectedError,
89+
select,
90+
selected_from,
7791
)
92+
from ._sender import Sender, SenderError
7893

7994
__all__ = [
8095
"Anycast",
81-
"Bidirectional",
8296
"Broadcast",
8397
"ChannelClosedError",
8498
"ChannelError",
8599
"Error",
86-
"Peekable",
100+
"Merge",
101+
"MergeNamed",
87102
"Receiver",
88103
"ReceiverError",
89-
"ReceiverInvalidatedError",
90104
"ReceiverStoppedError",
105+
"SelectError",
106+
"SelectErrorGroup",
107+
"Selected",
91108
"Sender",
92109
"SenderError",
93-
"util",
110+
"UnhandledSelectedError",
111+
"select",
112+
"selected_from",
94113
]

0 commit comments

Comments
 (0)