|
1 | 1 | # Frequenz Channels Release Notes |
2 | 2 |
|
3 | | -## Summary |
| 3 | +## Bug Fixes |
4 | 4 |
|
5 | | -The minimum Python supported version was bumped to 3.11 and the `Select` class replaced by the new `select()` function. |
6 | | - |
7 | | -## Upgrading |
8 | | - |
9 | | -* The minimum supported Python version was bumped to 3.11, downstream projects will need to upgrade too to use this version. |
10 | | - |
11 | | -* The `Select` class was replaced by a new `select()` function, with the following improvements: |
12 | | - |
13 | | - * Type-safe: proper type hinting by using the new helper type guard `selected_from()`. |
14 | | - * Fixes potential starvation issues. |
15 | | - * Simplifies the interface by providing values one-by-one. |
16 | | - * Guarantees there are no dangling tasks left behind when used as an async context manager. |
17 | | - |
18 | | - This new function is an [async iterator](https://docs.python.org/3.11/library/collections.abc.html#collections.abc.AsyncIterator), and makes sure no dangling tasks are left behind after a select loop is done. |
19 | | - |
20 | | - Example: |
21 | | - ```python |
22 | | - timer1 = Timer.periodic(datetime.timedelta(seconds=1)) |
23 | | - timer2 = Timer.timeout(datetime.timedelta(seconds=0.5)) |
24 | | - |
25 | | - async for selected in select(timer1, timer2): |
26 | | - if selected_from(selected, timer1): |
27 | | - # Beware: `selected.value` might raise an exception, you can always |
28 | | - # check for exceptions with `selected.exception` first or use |
29 | | - # a try-except block. You can also quickly check if the receiver was |
30 | | - # stopped and let any other unexpected exceptions bubble up. |
31 | | - if selected.was_stopped(): |
32 | | - print("timer1 was stopped") |
33 | | - continue |
34 | | - print(f"timer1: now={datetime.datetime.now()} drift={selected.value}") |
35 | | - timer2.stop() |
36 | | - elif selected_from(selected, timer2): |
37 | | - # Explicitly handling of exceptions |
38 | | - match selected.exception: |
39 | | - case ReceiverStoppedError(): |
40 | | - print("timer2 was stopped") |
41 | | - case Exception() as exception: |
42 | | - print(f"timer2: exception={exception}") |
43 | | - case None: |
44 | | - # All good, no exception, we can use `selected.value` safely |
45 | | - print( |
46 | | - f"timer2: now={datetime.datetime.now()} " |
47 | | - f"drift={selected.value}" |
48 | | - ) |
49 | | - case _ as unhanded: |
50 | | - assert_never(unhanded) |
51 | | - else: |
52 | | - # This is not necessary, as select() will check for exhaustiveness, but |
53 | | - # it is good practice to have it in case you forgot to handle a new |
54 | | - # receiver added to `select()` at a later point in time. |
55 | | - assert False |
56 | | - ``` |
57 | | - |
58 | | -## New Features |
59 | | - |
60 | | -* A new `select()` function was added, please look at the *Upgrading* section for details. |
61 | | - |
62 | | -* A new `Event` utility receiver was added. |
63 | | - |
64 | | - This receiver can be made ready manually. It is mainly useful for testing but can also become handy in scenarios where a simple, on-off signal needs to be sent to a select loop for example. |
65 | | - |
66 | | - Example: |
67 | | - |
68 | | - ```python |
69 | | - import asyncio |
70 | | - from frequenz.channels import Receiver |
71 | | - from frequenz.channels.util import Event, select, selected_from |
72 | | - |
73 | | - other_receiver: Receiver[int] = ... |
74 | | - exit_event = Event() |
75 | | - |
76 | | - async def exit_after_10_seconds() -> None: |
77 | | - asyncio.sleep(10) |
78 | | - exit_event.set() |
79 | | - |
80 | | - asyncio.ensure_future(exit_after_10_seconds()) |
81 | | - |
82 | | - async for selected in select(exit_event, other_receiver): |
83 | | - if selected_from(selected, exit_event): |
84 | | - break |
85 | | - if selected_from(selected, other_receiver): |
86 | | - print(selected.value) |
87 | | - else: |
88 | | - assert False, "Unknow receiver selected" |
89 | | - ``` |
90 | | - |
91 | | -* The `Timer` class now has more descriptive `__str__` and `__repr__` methods. |
| 5 | +* `Timer`: Fix bug that was causing calls to `reset()` to not reset the timer, if the timer was already being awaited. |
0 commit comments