Skip to content

Commit

Permalink
Add a new async iterable select() function
Browse files Browse the repository at this point in the history
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 <luca-frequenz@llucax.com>
  • Loading branch information
llucax committed Jun 16, 2023
1 parent 71306e7 commit 2764d01
Show file tree
Hide file tree
Showing 2 changed files with 412 additions and 4 deletions.
21 changes: 19 additions & 2 deletions src/frequenz/channels/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,23 @@
* [Select][frequenz.channels.util.Select]: A helper to select the next
available message for each [receiver][frequenz.channels.Receiver] in a group
of receivers.
* [select][frequenz.channels.util.select]: A function to iterate over a group
of [receivers][frequenz.channels.Receiver] and select the next available value.
"""

from ._file_watcher import FileWatcher
from ._merge import Merge
from ._merge_named import MergeNamed
from ._select import Select
from ._select import (
Select,
Selected,
SelectError,
SelectErrorGroup,
UnhandledSelectedError,
select,
selected_from,
)
from ._timer import (
MissedTickPolicy,
SkipMissedAndDrift,
Expand All @@ -42,9 +53,15 @@
"Merge",
"MergeNamed",
"MissedTickPolicy",
"Timer",
"Select",
"SelectError",
"SelectErrorGroup",
"Selected",
"SkipMissedAndDrift",
"SkipMissedAndResync",
"Timer",
"TriggerAllMissed",
"UnhandledSelectedError",
"select",
"selected_from",
]

0 comments on commit 2764d01

Please sign in to comment.