Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
## Bug Fixes

<!-- Here goes notable bug fixes that are worth a special mention or explanation -->

- Docstrings now use absolute cross-references to public paths instead of relative ones. Relative cross-references are expanded from the module where an object is defined since mkdocstrings-python 2.0.9, which broke some links, and projects rendering docstrings inherited from this library needed `relative_crossrefs: true` for them to work at all. See [frequenz-repo-config-python#655](https://github.com/frequenz-floss/frequenz-repo-config-python/issues/655) for details.
1 change: 0 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ plugins:
show_source: true
show_symbol_type_toc: true
signature_crossrefs: true
relative_crossrefs: true
inventories:
# See https://mkdocstrings.github.io/python/usage/#import for details
- https://docs.python.org/3/objects.inv
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ dev-mkdocs = [
"mkdocs-literate-nav == 0.6.3",
"mkdocs-macros-plugin == 1.5.0",
"mkdocs-material == 9.7.7",
"mkdocstrings[python] == 1.0.6",
"mkdocstrings[python] == 1.0.7",
"mkdocstrings-python == 2.0.7",
"pymdownx-superfence-filter-lines == 0.1.0",
"griffe==2.3.0",
Expand Down
42 changes: 21 additions & 21 deletions src/frequenz/channels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,71 +11,71 @@

Base classes:

* [`Receiver`][.Receiver]: An object that can wait for and
* [`Receiver`][frequenz.channels.Receiver]: An object that can wait for and
consume messages from a channel.

* [`Sender`][.Sender]: An object that can send messages to
* [`Sender`][frequenz.channels.Sender]: An object that can send messages to
a channel.

Channels:

* [`Anycast`][.Anycast]: A channel that supports multiple
* [`Anycast`][frequenz.channels.Anycast]: A channel that supports multiple
senders and multiple receivers. A message sent through a sender will be
received by exactly one receiver.

* [`Broadcast`][.Broadcast]: A channel to broadcast messages
* [`Broadcast`][frequenz.channels.Broadcast]: A channel to broadcast messages
from multiple senders to multiple receivers. Each message sent through any of
the senders is received by all of the receivers.

Utilities to work with channels:

* [`merge`][.merge]: Merge messages coming from multiple receivers into
* [`merge`][frequenz.channels.merge]: Merge messages coming from multiple receivers into
a single stream.

* [`select`][.select]: Iterate over the messages of all
[receivers][.Receiver] as new messages become available.
* [`select`][frequenz.channels.select]: Iterate over the messages of all
[receivers][frequenz.channels.Receiver] as new messages become available.

* [`LatestValueCache`][.LatestValueCache]: A cache that stores
* [`LatestValueCache`][frequenz.channels.LatestValueCache]: A cache that stores
the latest value in a receiver, providing a way to look up the latest value in
a stream, without having to wait, as long as there has been one value
received.

Exception classes:

* [`Error`][.Error]: Base class for all errors in this
* [`Error`][frequenz.channels.Error]: Base class for all errors in this
library.

* [`ChannelError`][.ChannelError]: Base class for all errors
* [`ChannelError`][frequenz.channels.ChannelError]: Base class for all errors
related to channels.

* [`ChannelClosedError`][.ChannelClosedError]: Error raised when
* [`ChannelClosedError`][frequenz.channels.ChannelClosedError]: Error raised when
trying to operate (send, receive, etc.) through a closed channel.

* [`SenderError`][.SenderError]: Base class for all errors
* [`SenderError`][frequenz.channels.SenderError]: Base class for all errors
related to senders.

* [`ReceiverError`][.ReceiverError]: Base class for all errors
* [`ReceiverError`][frequenz.channels.ReceiverError]: Base class for all errors
related to receivers.

* [`ReceiverStoppedError`][.ReceiverStoppedError]: A receiver
* [`ReceiverStoppedError`][frequenz.channels.ReceiverStoppedError]: A receiver
stopped producing messages.

* [`SelectError`][.SelectError]: Base class for all errors
related to [`select`][.select].
* [`SelectError`][frequenz.channels.SelectError]: Base class for all errors
related to [`select`][frequenz.channels.select].

* [`UnhandledSelectedError`][.UnhandledSelectedError]: An error
raised by [`select`][.select] that was not handled by the
* [`UnhandledSelectedError`][frequenz.channels.UnhandledSelectedError]: An error
raised by [`select`][frequenz.channels.select] that was not handled by the
user.

Extra utility receivers:

* [`Event`][.event.Event]: A receiver that generates a message when
* [`Event`][frequenz.channels.event.Event]: A receiver that generates a message when
an event is set.

* [`FileWatcher`][.file_watcher.FileWatcher]: A receiver that
* [`FileWatcher`][frequenz.channels.file_watcher.FileWatcher]: A receiver that
generates a message when a file is added, modified or deleted.

* [`Timer`][.timer.Timer]: A receiver that generates a message after a
* [`Timer`][frequenz.channels.timer.Timer]: A receiver that generates a message after a
given amount of time.
"""

Expand Down
36 changes: 20 additions & 16 deletions src/frequenz/channels/_anycast.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ class Anycast(Generic[ChannelMessageT]):

To create a new [senders][frequenz.channels.Sender] and
[receivers][frequenz.channels.Receiver] you can use the
[`.new_sender()`][.new_sender] and
[`.new_receiver()`][.new_receiver] methods
[`.new_sender()`][frequenz.channels.Anycast.new_sender] and
[`.new_receiver()`][frequenz.channels.Anycast.new_receiver] methods
respectively.

When the channel is not needed anymore, it should be closed with the
[`.aclose()`][.aclose] method. This will prevent further
[`.aclose()`][frequenz.channels.Anycast.aclose] method. This will prevent further
attempts to [`send()`][frequenz.channels.Sender.send] data. Receivers will still be
able to drain the pending messages on the channel, but after that, subsequent
[`receive()`][frequenz.channels.Receiver.receive] calls will raise a
Expand Down Expand Up @@ -265,7 +265,7 @@ def limit(self) -> int:
"""The maximum number of messages that can be stored in the channel's buffer.

If the length of channel's buffer reaches the limit, then the sender
blocks at the [`send()`][...Sender.send] method until
blocks at the [`send()`][frequenz.channels.Sender.send] method until
a message is consumed.
"""
maxlen = self._deque.maxlen
Expand All @@ -275,12 +275,12 @@ def limit(self) -> int:
async def aclose(self) -> None:
"""Close the channel.

Any further attempts to [`send()`][...Sender.send] data
will raise a [`SenderError`][...SenderError].
Any further attempts to [`send()`][frequenz.channels.Sender.send] data
will raise a [`SenderError`][frequenz.channels.SenderError].

Receivers will still be able to drain the pending messages on the channel,
but after that, subsequent
[`receive()`][...Receiver.receive] calls will return `None`
[`receive()`][frequenz.channels.Receiver.receive] calls will return `None`
immediately.
"""
self._closed = True
Expand All @@ -290,8 +290,11 @@ async def aclose(self) -> None:
self._recv_cv.notify_all()

@deprecated("The close() method is deprecated, use aclose() instead")
async def close(self) -> None: # noqa: D402
"""Close the channel, deprecated alias for [`.aclose()`][..aclose].""" # noqa: D402
async def close(self) -> None:
"""Close the channel.

Deprecated alias for [`.aclose()`][frequenz.channels.Anycast.aclose].
"""
return await self.aclose()

def new_sender(self) -> Sender[ChannelMessageT]:
Expand Down Expand Up @@ -351,7 +354,7 @@ async def send(self, message: _T, /) -> None:

Raises:
SenderError: If the underlying channel was closed.
A [`ChannelClosedError`][...ChannelClosedError] is
A [`ChannelClosedError`][frequenz.channels.ChannelClosedError] is
set as the cause.
SenderClosedError: If this sender was closed.
"""
Expand Down Expand Up @@ -387,7 +390,7 @@ async def aclose(self) -> None:

After closing, the sender will not be able to send any more messages. Any
attempt to send a message through a closed sender will raise a
[`SenderError`][...SenderError].
[`SenderError`][frequenz.channels.SenderError].
"""
self._closed = True

Expand Down Expand Up @@ -430,10 +433,11 @@ def __init__(self, channel: Anycast[_T], /) -> None:
async def ready(self) -> bool:
"""Wait until the receiver is ready with a message or an error.

Once a call to [`.ready()`][..ready] has finished, the message should be read with
a call to [`.consume()`][..consume] ([`receive()`][...Receiver.receive]
or iterated over). The receiver will
remain ready (this method will return immediately) until it is
Once a call to [`.ready()`][frequenz.channels.Receiver.ready] has finished, the
message should be read with a call to
[`.consume()`][frequenz.channels.Receiver.consume]
([`receive()`][frequenz.channels.Receiver.receive] or iterated over). The
receiver will remain ready (this method will return immediately) until it is
consumed.

Returns:
Expand All @@ -460,7 +464,7 @@ async def ready(self) -> bool:

@override
def consume(self) -> _T:
"""Return the latest message once [`ready()`][..ready] is complete.
"""Return the latest message once [`ready()`][frequenz.channels.Receiver.ready] is complete.

Returns:
The next message that was received.
Expand Down
63 changes: 35 additions & 28 deletions src/frequenz/channels/_broadcast.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ class Broadcast( # pylint: disable=too-many-instance-attributes

To create a new [senders][frequenz.channels.Sender] and
[receivers][frequenz.channels.Receiver] you can use the
[`.new_sender()`][.new_sender] and
[`.new_receiver()`][.new_receiver] methods
[`.new_sender()`][frequenz.channels.Broadcast.new_sender] and
[`.new_receiver()`][frequenz.channels.Broadcast.new_receiver] methods
respectively.

When a channel is not needed anymore, it should be closed with
[`.aclose()`][.aclose]. This will prevent further
[`.aclose()`][frequenz.channels.Broadcast.aclose]. This will prevent further
attempts to [`send()`][frequenz.channels.Sender.send] data, and will allow
receivers to drain the pending items on their queues, but after that,
subsequent [`receive()`][frequenz.channels.Receiver.receive] calls will
Expand Down Expand Up @@ -200,13 +200,13 @@ def __init__(
name: The name of the channel. This is for logging purposes, and it will be
shown in the string representation of the channel.
resend_latest: When True, every time a new receiver is created with
[`.new_receiver()`][..new_receiver], the last message seen by the channel
will be sent to the
new receiver automatically. This allows new receivers on slow streams to
get the latest message as soon as they are created, without having to
wait for the next message on the channel to arrive. It is safe to be
set in data/reporting channels, but is not recommended for use in
channels that stream control instructions.
[`.new_receiver()`][frequenz.channels.Broadcast.new_receiver], the last
message seen by the channel will be sent to the new receiver
automatically. This allows new receivers on slow streams to get the
latest message as soon as they are created, without having to wait for
the next message on the channel to arrive. It is safe to be set in
data/reporting channels, but is not recommended for use in channels that
stream control instructions.
auto_close: If True, the channel will be closed when all senders or all
receivers are closed.
"""
Expand Down Expand Up @@ -239,8 +239,8 @@ def __init__(
self.resend_latest: bool = resend_latest
"""Whether to resend the latest message to new receivers.

When `True`, every time a new receiver is created with [`.new_receiver()`][..new_receiver],
it will
When `True`, every time a new receiver is created with
[`.new_receiver()`][frequenz.channels.Broadcast.new_receiver], it will
automatically get sent the latest message on the channel. This allows new
receivers on slow streams to get the latest message as soon as they are created,
without having to wait for the next message on the channel to arrive.
Expand Down Expand Up @@ -270,12 +270,12 @@ def is_closed(self) -> bool:
async def aclose(self) -> None:
"""Close this channel.

Any further attempts to [`send()`][...Sender.send] data
will raise a [`SenderError`][...SenderError].
Any further attempts to [`send()`][frequenz.channels.Sender.send] data
will raise a [`SenderError`][frequenz.channels.SenderError].

Receivers will still be able to drain the pending items on their queues,
but after that, subsequent
[`receive()`][...Receiver.receive] calls will return `None`
[`receive()`][frequenz.channels.Receiver.receive] calls will return `None`
immediately.
"""
self._latest = None
Expand All @@ -284,8 +284,11 @@ async def aclose(self) -> None:
self._recv_cv.notify_all()

@deprecated("The close() method is deprecated, use aclose() instead")
async def close(self) -> None: # noqa: D402
"""Close the channel, deprecated alias for [`.aclose()`][..aclose].""" # noqa: D402
async def close(self) -> None:
"""Close the channel.

Deprecated alias for [`.aclose()`][frequenz.channels.Broadcast.aclose].
"""
return await self.aclose()

def new_sender(self) -> BroadcastSender[ChannelMessageT]:
Expand Down Expand Up @@ -340,7 +343,7 @@ class BroadcastSender(CloneableSubscribableSender[_T]):
"""A sender to send messages to the broadcast channel.

Should not be created directly, but through the
[`.clone()`][.clone]
[`.clone()`][frequenz.channels.BroadcastSender.clone]
method of an existing sender.
"""

Expand Down Expand Up @@ -372,7 +375,7 @@ async def send(self, message: _T, /) -> None:

Raises:
SenderError: If the underlying channel was closed.
A [`ChannelClosedError`][...ChannelClosedError] is
A [`ChannelClosedError`][frequenz.channels.ChannelClosedError] is
set as the cause.
SenderClosedError: If this sender was closed.
"""
Expand Down Expand Up @@ -408,7 +411,7 @@ async def aclose(self) -> None:

After a sender is closed, it can no longer be used to send messages. Any
attempt to send a message through a closed sender will raise a
[`SenderClosedError`][...SenderClosedError].
[`SenderClosedError`][frequenz.channels.SenderClosedError].
"""
if self._closed:
return
Expand Down Expand Up @@ -491,7 +494,7 @@ def __init__(
get dropped just in this receiver.

Args:
channel: A reference to the [`Broadcast`][...Broadcast]
channel: A reference to the [`Broadcast`][frequenz.channels.Broadcast]
channel that this receiver belongs to.
name: A name to identify the receiver in the logs. If `None` an
`id(self)`-based name will be used. This is only for debugging
Expand Down Expand Up @@ -549,10 +552,11 @@ def __len__(self) -> int:
async def ready(self) -> bool:
"""Wait until the receiver is ready with a message or an error.

Once a call to [`.ready()`][..ready] has finished, the message should be read with
a call to [`.consume()`][..consume] ([`receive()`][...Receiver.receive]
or iterated over). The receiver will
remain ready (this method will return immediately) until it is
Once a call to [`.ready()`][frequenz.channels.BroadcastReceiver.ready] has
finished, the message should be read with a call to
[`.consume()`][frequenz.channels.BroadcastReceiver.consume]
([`receive()`][frequenz.channels.Receiver.receive] or iterated over). The
receiver will remain ready (this method will return immediately) until it is
consumed.

Returns:
Expand Down Expand Up @@ -580,7 +584,10 @@ async def ready(self) -> bool:

@override
def consume(self) -> _T:
"""Return the latest message once [`ready()`][..ready] is complete.
"""Return the latest message once the receiver is ready.

[`ready()`][frequenz.channels.BroadcastReceiver.ready] must be called before each call to
[`consume()`][frequenz.channels.BroadcastReceiver.consume].

Returns:
The next message that was received.
Expand All @@ -603,7 +610,7 @@ def close(self) -> None:

After calling this method, new messages will not be received. Once the
receiver's buffer is drained, trying to receive a message will raise a
[`ReceiverStoppedError`][...ReceiverStoppedError].
[`ReceiverStoppedError`][frequenz.channels.ReceiverStoppedError].
"""
self._closed = True
self._channel._receivers.pop( # pylint: disable=protected-access
Expand Down Expand Up @@ -805,7 +812,7 @@ def __new__(
name: The name of the channel. This is for logging purposes, and it will be
shown in the string representation of the channel.
resend_latest: When True, every time a new receiver is created with
[`Broadcast.new_receiver()`][...Broadcast.new_receiver],
[`Broadcast.new_receiver()`][frequenz.channels.Broadcast.new_receiver],
the last message seen by the channel will be sent to the
new receiver automatically. This allows new receivers on slow streams to
get the latest message as soon as they are created, without having to
Expand Down
10 changes: 5 additions & 5 deletions src/frequenz/channels/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
# Exceptions

All exceptions generated by this library inherit from the
[`Error`][..Error] exception.
[`Error`][frequenz.channels.Error] exception.

Exceptions generated by channels inherit from the
[`ChannelError`][..ChannelError] exception. When there is an attempt to
use a closed channel, a [`ChannelClosedError`][..ChannelClosedError]
[`ChannelError`][frequenz.channels.ChannelError] exception. When there is an attempt to
use a closed channel, a [`ChannelClosedError`][frequenz.channels.ChannelClosedError]
exception is raised.

# Causes
Expand Down Expand Up @@ -40,8 +40,8 @@
Tip:
If you are using the async iteration interface for receivers, then you can
access the cause of the
[`ReceiverStoppedError`][..ReceiverStoppedError] exception by
explicitly calling [`receive()`][..Receiver.receive] on the
[`ReceiverStoppedError`][frequenz.channels.ReceiverStoppedError] exception by
explicitly calling [`receive()`][frequenz.channels.Receiver.receive] on the
receiver after the iteration is done:

```python show_lines="6:"
Expand Down
Loading