Skip to content

Commit a2410bb

Browse files
committed
Add a new async iterable select() function
This function will replace the current `Select` implementation with the following improvements: * Proper type hinting by using the new helper type guard `selected_from()`. * Fixes potential starvation issues. * Simplifies the interface by providing values one-by-one. * Simplifies the implementation, so it is easier to maintain. There are some other improvements we would have liked to be able to make but was difficult. For example, typing for `select()` is tricky. We had the idea of using a declarative design, something like: ```python class MySelector(Selector): receiver1: x.new_receiver() receiver2: y.new_receiver() async for selected in MySelector: if selected.receiver is receiver1: # Do something with selected.value elif selected.receiver is receiver1: # Do something with selected.value ``` This is similar to `Enum`, but `Enum` has special support in `mypy` that we can't have. With the current implementation, the typing could be slightly improved by using `TypeVarTuple`, but we are not because "transformations" are not supported yet, see: python/typing#1216 Also support for `TypeVarTuple` in general is still experimental (and very incomplete in `mypy`). With this we would also probably be able to properly type `select` and *maybe* even be able to leverage the exhaustiveness checking of `mypy` to make sure the selected value is narrowed down to the correct type to make sure all receivers are handled, with the help of `assert_never` as described in: https://docs.python.org/3.11/library/typing.html#typing.assert_never We also explored the possibility of using `match` to perform exhaustiveness checking, but we couldn't find a way to make it work with `match`, and `match` is not yet checked for exhaustiveness by `mypy` anyway, see: python/mypy#13597 Signed-off-by: Leandro Lucarella <[email protected]>
1 parent f69ff82 commit a2410bb

File tree

2 files changed

+413
-3
lines changed

2 files changed

+413
-3
lines changed

src/frequenz/channels/util/__init__.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,23 @@
2323
* [Select][frequenz.channels.util.Select]: A helper to select the next
2424
available message for each [receiver][frequenz.channels.Receiver] in a group
2525
of receivers.
26+
27+
* [select][frequenz.channels.util.select]: A function to iterate over a group
28+
of [receivers][frequenz.channels.Receiver] and select the next available value.
2629
"""
2730

2831
from ._file_watcher import FileWatcher
2932
from ._merge import Merge
3033
from ._merge_named import MergeNamed
31-
from ._select import Select
34+
from ._select import (
35+
Select,
36+
Selected,
37+
SelectError,
38+
SelectErrorGroup,
39+
UnhandledSelectedError,
40+
select,
41+
selected_from,
42+
)
3243
from ._timer import (
3344
MissedTickPolicy,
3445
SkipMissedAndDrift,
@@ -42,9 +53,15 @@
4253
"Merge",
4354
"MergeNamed",
4455
"MissedTickPolicy",
45-
"Timer",
4656
"Select",
57+
"SelectError",
58+
"SelectErrorGroup",
59+
"Selected",
4760
"SkipMissedAndDrift",
4861
"SkipMissedAndResync",
62+
"Timer",
4963
"TriggerAllMissed",
64+
"UnhandledSelectedError",
65+
"select",
66+
"selected_from",
5067
]

0 commit comments

Comments
 (0)