| 
2 | 2 | 
 
  | 
3 | 3 | ## Summary  | 
4 | 4 | 
 
  | 
5 |  | -<!-- Here goes a general summary of what this release is about -->  | 
 | 5 | +This is the first stable release of the Frequenz channels library.  | 
 | 6 | + | 
 | 7 | +If you are **upgrading from the previous 1.0.0 pre-releases**, please look into the release notes for those versions to see the changes and upgrade instructions:  | 
 | 8 | + | 
 | 9 | +* [1.0.0-rc.1](https://github.com/frequenz-floss/frequenz-channels-python/releases/tag/v1.0.0-rc.1)  | 
 | 10 | +* [1.0.0-beta.2](https://github.com/frequenz-floss/frequenz-channels-python/releases/tag/v1.0.0-beta.2)  | 
 | 11 | +* [1.0.0-beta.1](https://github.com/frequenz-floss/frequenz-channels-python/releases/tag/v1.0.0-beta.1)  | 
 | 12 | + | 
 | 13 | +There were no changes between 1.0.0-rc.1 and this 1.0.0 final release.  | 
 | 14 | + | 
 | 15 | +If you are **upgrading from v0.16.x**, please keep reading these release notes.  | 
6 | 16 | 
 
  | 
7 | 17 | ## Upgrading  | 
8 | 18 | 
 
  | 
9 |  | -<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->  | 
 | 19 | +* The following symbols were moved to the top-level `frequenz.channels` package:  | 
 | 20 | + | 
 | 21 | +  - `Selected`  | 
 | 22 | +  - `SelectError`  | 
 | 23 | +  - `UnhandledSelectedError`  | 
 | 24 | +  - `select`  | 
 | 25 | +  - `selected_from`  | 
 | 26 | + | 
 | 27 | +* `util`  | 
 | 28 | + | 
 | 29 | +  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).  | 
 | 30 | + | 
 | 31 | +* All exceptions that took `Any` as the `message` argument now take `str` instead.  | 
 | 32 | + | 
 | 33 | +  If you were passing a non-`str` value to an exception, you should convert it using `str(value)` before passing it to the exception.  | 
 | 34 | + | 
 | 35 | +* `Anycast`  | 
 | 36 | + | 
 | 37 | +  - `__init__`: The `maxsize` argument was renamed to `limit` and made keyword-only and a new keyword-only `name` (required) argument was added.  | 
 | 38 | + | 
 | 39 | +    You should instantiate using `Anycast(name=..., limit=...)` (or `Anycast(name=...)` if the default `limit` is enough) instead of `Anycast(...)` or `Anycast(maxsize=...)`.  | 
 | 40 | + | 
 | 41 | +  - The following properties were changed:  | 
 | 42 | + | 
 | 43 | +    - `limit`: is now read-only.  | 
 | 44 | +    - `closed`: is now named `is_closed` and read-only.  | 
 | 45 | + | 
 | 46 | +  - `new_sender` and `new_receiver`: They now return a base `Sender` and `Receiver` class (respectively) instead of a channel-specific `Sender` or `Receiver` subclass.  | 
 | 47 | + | 
 | 48 | +    This means users now don't have access to the internals to the channel-specific `Sender` and `Receiver` subclasses.  | 
 | 49 | + | 
 | 50 | +* `Broadcast`  | 
 | 51 | + | 
 | 52 | +  - `__init__`: The `name` and `resend_latest` arguments were made keyword-only.  | 
 | 53 | + | 
 | 54 | +    You should instantiate using `Broadcast(name=name, resend_latest=resend_latest)` (or `Broadcast()` if the defaults are enough) instead of `Broadcast(name)` or `Broadcast(name, resend_latest)`.  | 
 | 55 | + | 
 | 56 | +  - The following properties were changed:  | 
 | 57 | + | 
 | 58 | +    - `limit`: is now read-only.  | 
 | 59 | +    - `closed`: is now named `is_closed` and read-only.  | 
 | 60 | + | 
 | 61 | +  - `new_receiver`: The `maxsize` argument was renamed to `limit` and made keyword-only; the `name` argument was also made keyword-only. If a `name` is not specified, it will be generated from the `id()` of the instance instead of a random UUID.  | 
 | 62 | + | 
 | 63 | +    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)`.  | 
 | 64 | + | 
 | 65 | +  - `new_sender` and `new_receiver` now return a base `Sender` and `Receiver` class (respectively) instead of a channel-specific `Sender` or `Receiver` subclass.  | 
 | 66 | + | 
 | 67 | +    This means users now don't have access to the internals to the channel-specific `Sender` and `Receiver` subclasses.  | 
 | 68 | + | 
 | 69 | +* `Event`  | 
 | 70 | + | 
 | 71 | +  - Moved from `frequenz.channels.util` to `frequenz.channels.event`.  | 
 | 72 | + | 
 | 73 | +  - `__init__`: The `name` argument was made keyword-only. The default was changed to a more readable version of `id(self)`.  | 
 | 74 | + | 
 | 75 | +    You should instantiate using `Event(name=...)` instead of `Event(...)`.  | 
 | 76 | + | 
 | 77 | +* `FileWatcher`  | 
 | 78 | + | 
 | 79 | +  - Moved from `frequenz.channels.util` to `frequenz.channels.file_watcher`.  | 
 | 80 | + | 
 | 81 | +  - 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`).  | 
 | 82 | + | 
 | 83 | +* `Receiver`  | 
 | 84 | + | 
 | 85 | +  - The `map()` function now takes a positional-only argument, if you were using `receiver.map(call=fun)` you should replace it with `receiver.map(func)`.  | 
 | 86 | + | 
 | 87 | +* `SelectError` now inherits from `channels.Error` instead of `BaseException`, so you should be able to catch it with `except Exception:` or `except channels.Error:`.  | 
 | 88 | + | 
 | 89 | +* `Selected`  | 
 | 90 | + | 
 | 91 | +  - The `value` property was renamed to `message`.  | 
 | 92 | +  - `was_stopped` is now a property, you need to replace `selected.was_stopped()` with `selected.was_stopped`.  | 
 | 93 | + | 
 | 94 | +* `Sender`  | 
 | 95 | + | 
 | 96 | +  - The `send` method now takes a positional-only argument, if you were using `sender.send(msg=message)` you should replace it with `sender.send(message)`.  | 
 | 97 | + | 
 | 98 | +* `Timer` and support classes  | 
 | 99 | + | 
 | 100 | +  - Moved from `frequenz.channels.util` to `frequenz.channels.timer`.  | 
 | 101 | + | 
 | 102 | +### Removals  | 
 | 103 | + | 
 | 104 | +* `Anycast`  | 
 | 105 | + | 
 | 106 | +  - The following public properties were removed (made private): `deque`, `send_cv`, `recv_cv`.  | 
 | 107 | + | 
 | 108 | +* `Bidirectional`  | 
 | 109 | + | 
 | 110 | +  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.  | 
 | 111 | + | 
 | 112 | +* `Broadcast`  | 
 | 113 | + | 
 | 114 | +  - The following public properties were removed (made private): `recv_cv`, `receivers`.  | 
 | 115 | +  - `new_peekable()` was removed because `Peekable` was removed.  | 
 | 116 | + | 
 | 117 | +* `Merge`  | 
 | 118 | + | 
 | 119 | +  Replaced by the new `merge()` function. When replacing `Merge` with `merge()` please keep in mind that this new function will raise a `ValueError` if no receivers are passed to it.  | 
 | 120 | + | 
 | 121 | +  Please note that the old `Merge` class is still also available but it was renamed to `Merger` to avoid confusion with the new `merge()` function, but it is only present for typing reasons and should not be used directly.  | 
 | 122 | + | 
 | 123 | +* `MergeNamed`  | 
 | 124 | + | 
 | 125 | +  This class was redundant, use either the new `merge()` function or `select()` instead.  | 
 | 126 | + | 
 | 127 | +* `Peekable`  | 
 | 128 | + | 
 | 129 | +  This class was removed because it was merely a shortcut to a receiver that caches the last message received. It did not fit the channel abstraction well and was infrequently used.  | 
 | 130 | + | 
 | 131 | +  You can replace it with a task that receives and retains the last message.  | 
 | 132 | + | 
 | 133 | +* `Receiver.into_peekable()` was removed because `Peekable` was removed.  | 
 | 134 | + | 
 | 135 | +* `ReceiverInvalidatedError` was removed because it was only used when converting to a `Peekable` and `Peekable` was removed.  | 
 | 136 | + | 
 | 137 | +* `SelectErrorGroup` was removed, a Python built-in `BaseExceptionGroup` is raised instead in case of unexpected errors while finalizing a `select()` loop, which will be automatically converted to a simple `ExceptionGroup` when no exception in the groups is a `BaseException`.  | 
 | 138 | + | 
 | 139 | +- `Timer`:  | 
 | 140 | + | 
 | 141 | +  - `periodic()` and `timeout()`: The names proved to be too confusing, please use `Timer()` and pass a missing ticks policy explicitly instead. In general you can update your code by doing:  | 
 | 142 | + | 
 | 143 | +    * `Timer.periodic(interval)` / `Timer.periodic(interval, skip_missed_ticks=True)` -> `Timer(interval, TriggerAllMissed())`  | 
 | 144 | +    * `Timer.periodic(interval, skip_missed_ticks=False)` -> `Timer(interval, SkipMissedAndResync())`  | 
 | 145 | +    * `Timer.timeout(interval)` -> `Timer(interval, SkipMissedAndDrift())`  | 
10 | 146 | 
 
  | 
11 | 147 | ## New Features  | 
12 | 148 | 
 
  | 
13 |  | -<!-- Here goes the main new features and examples or instructions on how to use them -->  | 
 | 149 | +* A new `Broadcast.resend_latest` read-write property was added to get/set whether the latest message should be resent to new receivers.  | 
 | 150 | + | 
 | 151 | +* `Timer()` and `Timer.reset()` now take an optional `start_delay` option to make the timer start after some delay.  | 
 | 152 | + | 
 | 153 | +  This can be useful, for example, if the timer needs to be *aligned* to a particular time. The alternative to this would be to `sleep()` for the time needed to align the timer, but if the `sleep()` call gets delayed because the event loop is busy, then a re-alignment is needed and this could go on for a while. The only way to guarantee a certain alignment (with a reasonable precision) is to delay the timer start.  | 
 | 154 | + | 
 | 155 | +## Improvements  | 
 | 156 | + | 
 | 157 | +* The arm64 architecture is now officially supported.  | 
 | 158 | + | 
 | 159 | +* A new User's Guide was added to the documentation and the documentation was greately improved in general.  | 
 | 160 | + | 
 | 161 | +* A new `merge()` function was added to replace `Merge`.  | 
 | 162 | + | 
 | 163 | +* A warning will be logged by `Anycast` channels if senders are blocked because the channel buffer is full.  | 
 | 164 | + | 
 | 165 | +* `Receiver`, `merge`/`Merger`, `Error` and its derived classes now use a covariant generic type, which allows the generic type to be broader than the actual type.  | 
 | 166 | + | 
 | 167 | +* `Sender` now uses a contravariant generic type, which allows the generic type to be narrower than the required type.  | 
 | 168 | + | 
 | 169 | +* `ChannelError` is now generic, so when accessing the `channel` attribute, the type of the channel is preserved.  | 
 | 170 | + | 
 | 171 | +* Most classes have now a better implementation of `__str__` and `__repr__`.  | 
14 | 172 | 
 
  | 
15 | 173 | ## Bug Fixes  | 
16 | 174 | 
 
  | 
17 |  | -<!-- Here goes notable bug fixes that are worth a special mention or explanation -->  | 
 | 175 | +* `Timer`: Fix bug that was causing calls to `reset()` to not reset the timer, if the timer was already being awaited.  | 
0 commit comments