Releases: frequenz-floss/frequenz-channels-python
v0.16.1
Frequenz Channels Release Notes
Bug Fixes
Timer
: Fix bug that was causing calls toreset()
to not reset the timer, if the timer was already being awaited.
What's Changed
Full Changelog: v0.16.0...v0.16.1
v1.0.0-beta.2
Frequenz channels Release Notes
Summary
This release only have a CI fix that prevented v1.0.0-beta.1
to be automatically published. There are no user-visible changes.
What's Changed
Full Changelog: v1.0.0-beta.1...v1.0.0-beta.2
v1.0.0-beta.1
Frequenz channels Release Notes
Summary
The Timer
now can be started with a delay and some channel attributes were made private.
Upgrading
-
Anycast
- The following public properties were removed (made private):
limit
,closed
,deque
,send_cv
,recv_cv
.
- The following public properties were removed (made private):
-
Broadcast
- The following public properties were removed (made private):
name
,closed
,recv_cv
,receivers
.
- The following public properties were removed (made private):
New Features
-
The arm64 architecture is now officially supported.
-
The documentation was improved to:
- Show signatures with types.
- Show the inherited members.
- Documentation for pre-releases are now published.
- Show the full tag name as the documentation version.
- All development branches now have their documentation published (there is no
next
version anymore). - Fix the order of the documentation versions.
-
Broadcast
- Added a
resend_latest
read-write property to get/set whether the latest message should be resent to new receivers.
- Added a
-
Timer
-
Timer()
,Timer.timeout()
,Timer.periodic()
andTimer.reset()
now take an optionalstart_delay
option to make the timer start after some delay.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 thesleep()
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.
-
What's Changed
- Migrate to use repo-config by @llucax in #124
- Bump pylint from 2.17.3 to 2.17.4 by @dependabot in #125
- Bump pytest-mock from 3.10.0 to 3.11.1 by @dependabot in #126
- Bump mkdocs-material from 9.1.17 to 9.1.18 by @dependabot in #128
- Bump pytest from 7.3.1 to 7.4.0 by @dependabot in #127
- Bump mypy from 1.2.0 to 1.4.1 by @dependabot in #129
- Bump hypothesis from 6.80.0 to 6.80.1 by @dependabot in #130
- Bump actions/labeler from 4.2.0 to 4.3.0 by @dependabot in #131
- Bump black from 23.3.0 to 23.7.0 by @dependabot in #132
- Bump hypothesis from 6.80.1 to 6.81.1 by @dependabot in #133
- Bump pytest-asyncio from 0.21.0 to 0.21.1 by @dependabot in #135
- Improve documentation by @llucax in #136
- Bump sybil from 5.0.2 to 5.0.3 by @dependabot in #137
- Bump hypothesis from 6.81.1 to 6.81.2 by @dependabot in #138
- Bump mkdocs-material from 9.1.18 to 9.1.19 by @dependabot in #140
- Fix typo recever -> receiver by @Marenz in #139
- Bump hypothesis from 6.81.2 to 6.82.0 by @dependabot in #141
- Bump pylint from 2.17.4 to 2.17.5 by @dependabot in #143
- Bump mkdocs-material from 9.1.19 to 9.1.21 by @dependabot in #144
- Update to repo-config 0.4.0 by @Marenz in #142
- Bump hypothesis from 6.82.0 to 6.82.2 by @dependabot in #147
- Add optional start delay to
Timer
by @llucax in #145 - Bump hypothesis from 6.82.2 to 6.82.3 by @dependabot in #148
- Bump mypy from 1.4.1 to 1.5.0 by @dependabot in #149
- Bump hypothesis from 6.82.3 to 6.82.4 by @dependabot in #150
- Bump mypy from 1.5.0 to 1.5.1 by @dependabot in #152
- Bump hypothesis from 6.82.4 to 6.82.5 by @dependabot in #155
- Bump hypothesis from 6.82.5 to 6.82.6 by @dependabot in #157
- Bump mkdocs-material from 9.1.21 to 9.2.1 by @dependabot in #158
- Bump mkdocs-material from 9.2.1 to 9.2.3 by @dependabot in #159
- Update watchfiles requirement from <0.20.0,>=0.15.0 to >=0.15.0,<0.21.0 by @dependabot in #160
- Bump mkdocs-material from 9.2.3 to 9.2.5 by @dependabot in #161
- Bump hypothesis from 6.82.6 to 6.84.0 by @dependabot in #171
- Bump mkdocstrings[python] from 0.22.0 to 0.23.0 by @dependabot in #167
- Bump mkdocs-material from 9.2.5 to 9.2.8 by @dependabot in #172
- Bump hypothesis from 6.84.0 to 6.84.1 by @dependabot in #173
- Bump mkdocs-section-index from 0.3.5 to 0.3.6 by @dependabot in #174
- Bump actions/checkout from 3 to 4 by @dependabot in #170
- Bump pytest from 7.4.0 to 7.4.1 by @dependabot in #169
- Bump hypothesis from 6.84.1 to 6.84.2 by @dependabot in #175
- Replace obsolete types by @daniel-zullo-frequenz in #176
- Upgrade to repo-config v0.5.2 by @llucax in #153
- Bump mkdocs-literate-nav from 0.6.0 to 0.6.1 by @dependabot in #179
- Bump pytest from 7.4.1 to 7.4.2 by @dependabot in #178
- Bump mkdocs-material from 9.2.8 to 9.3.1 by @dependabot in #183
- Bump hypothesis from 6.84.2 to 6.84.3 by @dependabot in #181
- Bump black from 23.7.0 to 23.9.1 by @dependabot in #180
- Bump mkdocs-section-index from 0.3.6 to 0.3.7 by @dependabot in #184
- docs: Improve README and intro by @llucax in #189
- Document class and module attributes by @daniel-zullo-frequenz in #185
- Bump hypothesis from 6.84.3 to 6.86.2 by @dependabot in #192
- Bump mkdocs-material from 9.3.1 to 9.3.2 by @dependabot in #191
- Bump hypothesis from 6.86.2 to 6.87.1 by @dependabot in #200
- Bump pylint from 2.17.5 to 2.17.7 by @dependabot in #201
- Bump hypothesis from 6.87.1 to 6.87.3 by @dependabot in #206
- Make internal variable names private by @shsms in #213
- Bump hypothesis from 6.87.3 to 6.88.0 by @dependabot in #210
- Update watchfiles requirement from <0.21.0,>=0.15.0 to >=0.15.0,<0.22.0 by @dependabot in #211
- Bump mypy from 1.5.1 to 1.6.0 by @dependabot in #208
- Bump mypy from 1.6.0 to 1.6.1 by @dependabot in #216
- Bump pytest from 7.4.2 to 7.4.3 by @dependabot in #220
- Bump hypothesis from 6.88.0 to 6.88.1 by @dependabot in #214
- Bump pytest-mock from 3.11.1 to 3.12.0 by @dependabot in #217
- Bump mkdocs-section-index from 0.3.7 to 0.3.8 by @dependabot in #196
- Bump black from 23.9.1 to 23.10.1 by @dependabot in #219
- Make
resend_latest
a public attribute forBroadcast
channels by @shsms in #221 - ...
v0.16.0
Frequenz Channels Release Notes
Summary
The minimum Python supported version was bumped to 3.11 and the Select
class replaced by the new select()
function.
Upgrading
-
The minimum supported Python version was bumped to 3.11, downstream projects will need to upgrade too to use this version.
-
The
Select
class was replaced by a newselect()
function, with the following improvements:- Type-safe: 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.
- Guarantees there are no dangling tasks left behind when used as an async context manager.
This new function is an async iterator, and makes sure no dangling tasks are left behind after a select loop is done.
Example:
timer1 = Timer.periodic(datetime.timedelta(seconds=1)) timer2 = Timer.timeout(datetime.timedelta(seconds=0.5)) async for selected in select(timer1, timer2): if selected_from(selected, timer1): # Beware: `selected.value` might raise an exception, you can always # check for exceptions with `selected.exception` first or use # a try-except block. You can also quickly check if the receiver was # stopped and let any other unexpected exceptions bubble up. if selected.was_stopped(): print("timer1 was stopped") continue print(f"timer1: now={datetime.datetime.now()} drift={selected.value}") timer2.stop() elif selected_from(selected, timer2): # Explicitly handling of exceptions match selected.exception: case ReceiverStoppedError(): print("timer2 was stopped") case Exception() as exception: print(f"timer2: exception={exception}") case None: # All good, no exception, we can use `selected.value` safely print( f"timer2: now={datetime.datetime.now()} " f"drift={selected.value}" ) case _ as unhanded: assert_never(unhanded) else: # This is not necessary, as select() will check for exhaustiveness, but # it is good practice to have it in case you forgot to handle a new # receiver added to `select()` at a later point in time. assert False
- Type-safe: proper type hinting by using the new helper type guard
New Features
-
A new
select()
function was added, please look at the Upgrading section for details. -
A new
Event
utility receiver was added.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.
Example:
import asyncio from frequenz.channels import Receiver from frequenz.channels.util import Event, select, selected_from other_receiver: Receiver[int] = ... exit_event = Event() async def exit_after_10_seconds() -> None: asyncio.sleep(10) exit_event.set() asyncio.ensure_future(exit_after_10_seconds()) async for selected in select(exit_event, other_receiver): if selected_from(selected, exit_event): break if selected_from(selected, other_receiver): print(selected.value) else: assert False, "Unknow receiver selected"
-
The
Timer
class now has more descriptive__str__
and__repr__
methods.
What's Changed
- Clear release notes by @leandro-lucarella-frequenz in #106
- Enable merge queue by @leandro-lucarella-frequenz in #108
- Bump actions/labeler from 4.0.3 to 4.0.4 by @dependabot in #110
- Add possible values to timer missed_tick_policy parameter documentation by @Marenz in #109
- Bump mkdocstrings[python] from 0.21.2 to 0.22.0 by @dependabot in #111
- Bump mkdocs-material from 9.1.14 to 9.1.15 by @dependabot in #112
- Bump the minimum Python supported version to 3.11 by @leandro-lucarella-frequenz in #113
- Bump actions/labeler from 4.0.4 to 4.1.0 by @dependabot in #115
- Bump mkdocs-material from 9.1.15 to 9.1.16 by @dependabot in #117
- Bump mkdocs-material from 9.1.16 to 9.1.17 by @dependabot in #118
- Bump actions/labeler from 4.1.0 to 4.2.0 by @dependabot in #119
- Replace
Select
withselect()
, a type-safe async iterator by @leandro-lucarella-frequenz in #114 - Fix broken cross-reference in docs by @llucax in #120
- Fix typo in release notes example by @llucax in #121
- Run CI for tags too by @llucax in #122
New Contributors
Full Changelog: v0.15.1...v0.16.0
v0.15.1
Frequenz Channels Release Notes
Summary
This is a bugfix release that mainly uses more up to date dependencies and extend the range of supported dependencies. There is technically one breaking change though, but this is hardly used by anyone.
Upgrading
FileWatcher
no longer accepts or setsNone
as theevent_types
argument. Instead, all available event types are now set by default while still providing the flexibility to customize the event types as needed.
Bug Fixes
- Many documentation examples were fixed.
What's Changed
- Clear release notes by @leandro-lucarella-frequenz in #90
- Add dependabot config and pin dependencies by @leandro-lucarella-frequenz in #92
- Bump mkdocs-section-index from 0.3.4 to 0.3.5 by @dependabot in #98
- Bump frequenz-floss/setup-git-user from 1 to 2 by @dependabot in #100
- Bump mkdocs-literate-nav from 0.4.0 to 0.6.0 by @dependabot in #94
- Merge various dependency updates by @leandro-lucarella-frequenz in #102
- Bump mkdocs-material from 8.5.7 to 9.1.12 by @dependabot in #97
- Bump mkdocs-material from 9.1.12 to 9.1.13 by @dependabot in #103
- Update event_types parameter in FileWatcher by @daniel-zullo-frequenz in #89
- Bump mkdocs-material from 9.1.13 to 9.1.14 by @dependabot in #104
- Validate docstring examples by @Marenz in #101
- Update release notes for v0.15.1 by @leandro-lucarella-frequenz in #105
New Contributors
- @dependabot made their first contribution in #98
- @Marenz made their first contribution in #101
Full Changelog: v0.15.0...v0.15.1
v0.15.0
Frequenz Channels Release Notes
Summary
This release adds support to pass None
values via channels and revamps the Timer
class to support custom policies for handling missed ticks and use the loop monotonic clock. There is also a fix for the FileWatcher
which includes a change in behavior when reporting changes for deleted files.
Upgrading
-
util.Timer
was replaced by a more generic implementation that allows for customizable policies to handle missed ticks.If you were using
Timer
to implement timeouts, these two pieces of code should be almost equivalent:-
Old:
old_timer = Timer(1.0) triggered_datetime = old_timer.receive()
-
New:
new_timer = Timer.timeout(timedelta(seconds=1.0)) drift = new_timer.receive() triggered_datetime = datetime.now(timezone.utc) - drift
They are not exactly the same because the
triggered_datetime
in the second case will not be exactly when the timer had triggered, but that shouldn't be relevant, the important part is when your code can actually react to the timer trigger and to know how much drift there was to be able to take corrective actions.Also the new
Timer
uses theasyncio
loop monotonic clock and the old one used the wall clock (datetime.now()
) to track time. This means that when usingasync-solipsism
to test, the newTimer
will always trigger immediately regardless of the state of the wall clock. This also means that we don't need to mock the wall clock withtime-machine
either now.With the previous timer one needed to create a separate task to run the timer, because otherwise it would block as it loops until the wall clock was at a specific time. Now the code will run like this:
timer = Timer.timeout(timedelta(seconds=1.0)) asyncio.sleep(0.5) # Advances the loop monotonic clock by 0.5 seconds immediately await drift = timer.receive() # Advances the clock by 0.5 immediately too assert drift == approx(timedelta(0)) # Because it could trigger exactly at the tick time # Simulates a delay in the timer trigger time asyncio.sleep(1.5) # Advances the loop monotonic clock by 1.5 seconds immediately await drift = timer.receive() # The timer should have triggered 0.5 seconds ago, so it doesn't even sleep assert drift == approx(timedelta(seconds=0.5)) # Now we should observe a drift of 0.5 seconds
Note: Before replacing this code blindly in all uses of
Timer.timeout()
, please consider using the periodic timer constructorTimer.periodic()
if you need a timer that triggers reliable on a periodic fashion, as the oldTimer
(andTimer.timeout()
) accumulates drift, which might not be what you want. -
-
FileWatcher
now will emit events even if the file doesn't exist anymore.Because the underlying library has a considerable delay in triggering filesystem events, it can happen that, for example, a
CREATE
event is received but at the time of receiving the file doesn't exist anymore (because if was removed just after creation and before the event was triggered).Before the
FileWatcher
will only emit events when the file exists, but this doesn't work forDELETE
events (clearly). Given the nature of this mechanism can lead to races easily, it is better to leave it to the user to decide when these situations happen and just report all events.Therefore, you should now check a file receiving an event really exist before trying to operate on it.
-
FileWatcher
reports the type of event observed in addition to the file path.Previously, only the file path was reported. With this update, users can now determine if a file change is a creation, modification, or deletion.
Note that this change may require updates to your existing code that relies onFileWatcher
as the new interface returns aFileWatcher.Event
instead of just the file path.
New Features
-
util.Timer
was replaced by a more generic implementation that allows for customizable policies to handle missed ticks. -
Passing
None
values via channels is now supported. -
FileWatcher.Event
was added to notify events when a file is created, modified, or deleted.
Bug Fixes
-
util.Select
/util.Merge
/util.MergeNamed
: Cancel pending tasks in__del__
methods only if possible (the loop is not already closed). -
FileWatcher
will now reportDELETE
events correctly.Due to a bug, before this release
DELETE
events were only reported if the file was re-created before the event was triggered.
What's Changed
- Clear release notes by @leandro-lucarella-frequenz in #81
- Publish to PyPi using trusted publishing by @leandro-lucarella-frequenz in #84
- Add support/tests for None-value channels by @sahas-subramanian-frequenz in #83
- Cancel pending tasks in
__del__
methods only if possible by @sahas-subramanian-frequenz in #85 - Make the timer generic and support periodic timers by @leandro-lucarella-frequenz in #79
- Fix
FileWatcher
DELETE
event reporting and rearrange tests by @leandro-lucarella-frequenz in #86 - Report the type of change observed in FileWatcher by @daniel-zullo-frequenz in #87
New Contributors
- @sahas-subramanian-frequenz made their first contribution in #83
- @daniel-zullo-frequenz made their first contribution in #87
Full Changelog: v0.14.0...v0.15.0
v0.14.0
Frequenz Channels Release Notes
Summary
The main change in this release is the revamp of exception handling in general. New exceptions were created and send()
now raises an exception too when it fails.
Hopefully they are now used much more uniformly across the whole library.
Upgrading
-
The
Sender.send()
method nowraise
s aSenderError
instead of returningFalse
. TheSenderError
will typically have aChannelClosedError
and the underlying reason as a chained exception. -
The
Receiver.ready()
method (and relatedreceive()
and__anext__
when used as an async iterator) nowraise
s aReceiverError
and in particular aReceiverStoppedError
when the receiver has no more messages to receive.Receiver.consume()
doesn't raise any exceptions.Receivers raising
EOFError
now raiseReceiverInvalidatedError
instead. -
For channels which senders raise an error when the channel is closed or which receivers stop receiving when the channel is closed, the
SenderError
andReceiverStoppedError
are chained with a__cause__
that is aChannelClosedError
with the channel that was closed. -
ChannelClosedError
now requires the argumentchannel
(before it was optional). -
Now exceptions are not raised in Receiver.ready() but in Receiver.consume() (receive() or the async iterator
anext
).
New Features
-
New exceptions were added:
-
Error
: A base exception from which all exceptions from this library inherit. -
SendError
: Raised for errors when sending messages. -
ReceiverError
: Raised for errors when receiving messages. -
ReceiverClosedError
: Raised when a receiver don't have more messages to receive. -
ReceiverInvalidatedError
: Raised when a receiver was invalidated (for example it was converted into aPeekable
).
-
What's Changed
- Clean release notes by @ela-kotulska-frequenz in #69
- Fix build badge by @leandro-lucarella-frequenz in #71
- Improve and add exceptions by @leandro-lucarella-frequenz in #61
- Make assert msg grammatically correct and less ambiguous by @mathias-baumann-frequenz in #74
- Finish release notes for v0.14.0 by @leandro-lucarella-frequenz in #80
New Contributors
- @mathias-baumann-frequenz made their first contribution in #74
Full Changelog: v0.13.0...v0.14.0
v0.13.0
Frequenz Channels Release Notes
New Features
- Add method to stop
Merge
andMergeNamed
.
What's Changed
- Clean release notes by @ela-kotulska-frequenz in #67
- Add stop method for MergeNamed and Merge channels by @ela-kotulska-frequenz in #68
Full Changelog: v0.12.0...v0.13.0
v0.12.0
Frequenz Channels Release Notes
Summary
Upgrading
New Features
- Add method to stop Select.
Bug Fixes
- Deactivate Broadcast receivers that were transformed to peekable.
What's Changed
- Clear release notes by @leandro-lucarella-frequenz in #58
- Add method to stop Select by @ela-kotulska-frequenz in #63
- Add a private method to deactivate Broadcast receivers by @shsms in #64
- Update release notes by @ela-kotulska-frequenz in #65
New Contributors
- @ela-kotulska-frequenz made their first contribution in #63
Full Changelog: v0.11.0...v0.12.0
v0.11.0
Frequenz Channels Release Notes
Summary
The project has a new home!
https://frequenz-floss.github.io/frequenz-channels-python/
For now the documentation is pretty scarce but we will be improving it with time.
Upgrading (breaking changes)
-
You need to make sure to use timezone-aware
datetime
objects when using the timestamp returned byTimer
, Otherwise you will get an exception. -
Channels methods
get_receiver()
andget_sender()
have been renamed tonew_receiver()
andnew_sender()
respectively. This is to make it more clear that new objects are being created. -
The public API surface has been reduced considerably to make it more clear where to import symbols. You should update your imports. The new symbol locations are:
frequenz.channels.Anycast
frequenz.channels.Broadcast
frequenz.channels.Anycast
frequenz.channels.Bidirectional
frequenz.channels.Broadcast
frequenz.channels.Peekable
frequenz.channels.Receiver
frequenz.channels.Sender
frequenz.channels.util.Merge
frequenz.channels.util.MergeNamed
frequenz.channels.util.FileWatcher
frequenz.channels.util.Select
frequenz.channels.util.Timer
-
The class
BufferedReceiver
was removed because the interface was really intended for channel implementations. Users are not supposed to enqueue messages to receiver but just receive from them. If you used it you can implement it yourself. -
The class
BidirectionalHandle
was moved toBidirectional.Handle
. -
The class
EventType
was moved toFileWatcher.EventType
.
New Features
- Python 3.11 is now supported!
Bug Fixes
-
Broadcast
receivers now get cleaned up once they go out of scope. -
Timer
now returns timezone-awaredatetime
objects using UTC as timezone.
What's Changed
- Fix project URLs by @leandro-lucarella-frequenz in #18
- ci: Test also Python 3.11 pre-releases by @leandro-lucarella-frequenz in #19
- Remove
version:xxx
and addpart:select
andpart:receivers
labels by @leandro-lucarella-frequenz in #28 - Use MkDocs to build the documentation by @leandro-lucarella-frequenz in #25
- ci: Add contents permission to publish-docs by @leandro-lucarella-frequenz in #33
- ci: Update aliases when deploying with mike by @leandro-lucarella-frequenz in #38
- Add PyPI and Docs badges to README by @shsms in #34
- ci: Use the final Python 3.11 version by @leandro-lucarella-frequenz in #39
- docs: Replace the Home with the README by @leandro-lucarella-frequenz in #40
- Fix CONTRIBUTING headings by @leandro-lucarella-frequenz in #41
- Use "aware" datetimes by @leandro-lucarella-frequenz in #48
- Ensure deleted receivers get cleaned up by @shsms in #45
- Avoid dropping of messages after breaking from
Select
blocks by @shsms in #42 - Rename
get_{sender,receiver}
tonew_{sender,receiver}
by @leandro-lucarella-frequenz in #49 - Clean up public API by @leandro-lucarella-frequenz in #55
- FileWatcher: Don't type-check
changes
by @leandro-lucarella-frequenz in #56 - Add Python 3.11 support to RELEASE_NOTES by @leandro-lucarella-frequenz in #57
Full Changelog: v0.10.0...v0.11.0