Skip to content

Commit ef2dde8

Browse files
Document class and module attributes (#185)
Add or amend docstrings for all classes and modules where needed. Fixes #31
2 parents cba3e18 + 17d91b7 commit ef2dde8

18 files changed

+120
-36
lines changed

src/frequenz/channels/_anycast.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,42 @@ def __init__(self, maxsize: int = 10) -> None:
6868
maxsize: Size of the channel's buffer.
6969
"""
7070
self.limit: int = maxsize
71+
"""The maximum number of values that can be stored in the channel's buffer.
72+
73+
If the length of channel's buffer reaches the limit, then the sender
74+
blocks at the [send()][frequenz.channels.Sender.send] method until
75+
a value is consumed.
76+
"""
77+
7178
self.deque: Deque[T] = deque(maxlen=maxsize)
79+
"""The channel's buffer."""
80+
7281
self.send_cv: Condition = Condition()
82+
"""The condition to wait for free space in the channel's buffer.
83+
84+
If the channel's buffer is full, then the sender waits for values to
85+
get consumed using this condition until there's some free space
86+
available in the channel's buffer.
87+
"""
88+
7389
self.recv_cv: Condition = Condition()
90+
"""The condition to wait for values in the channel's buffer.
91+
92+
If the channel's buffer is empty, then the receiver waits for values
93+
using this condition until there's a value available in the channel's
94+
buffer.
95+
"""
96+
7497
self.closed: bool = False
98+
"""Whether the channel is closed."""
7599

76100
async def close(self) -> None:
77101
"""Close the channel.
78102
79103
Any further attempts to [send()][frequenz.channels.Sender.send] data
80104
will return `False`.
81105
82-
Receivers will still be able to drain the pending items on the channel,
106+
Receivers will still be able to drain the pending values on the channel,
83107
but after that, subsequent
84108
[receive()][frequenz.channels.Receiver.receive] calls will return `None`
85109
immediately.
@@ -111,7 +135,7 @@ def new_receiver(self) -> Receiver[T]:
111135
class Sender(BaseSender[T]):
112136
"""A sender to send messages to an Anycast channel.
113137
114-
Should not be created directly, but through the `Anycast.ggetet_sender()`
138+
Should not be created directly, but through the `Anycast.new_sender()`
115139
method.
116140
"""
117141

@@ -122,6 +146,7 @@ def __init__(self, chan: Anycast[T]) -> None:
122146
chan: A reference to the channel that this sender belongs to.
123147
"""
124148
self._chan = chan
149+
"""The channel that this sender belongs to."""
125150

126151
async def send(self, msg: T) -> None:
127152
"""Send a message across the channel.
@@ -169,6 +194,8 @@ def __init__(self, chan: Anycast[T]) -> None:
169194
chan: A reference to the channel that this receiver belongs to.
170195
"""
171196
self._chan = chan
197+
"""The channel that this receiver belongs to."""
198+
172199
self._next: T | type[_Empty] = _Empty
173200

174201
async def ready(self) -> bool:
@@ -211,7 +238,7 @@ def consume(self) -> T:
211238

212239
assert (
213240
self._next is not _Empty
214-
), "`consume()` must be preceeded by a call to `ready()`"
241+
), "`consume()` must be preceded by a call to `ready()`"
215242
# mypy doesn't understand that the assert above ensures that self._next is not
216243
# _Sentinel. So we have to use a type ignore here.
217244
next_val: T = self._next # type: ignore[assignment]

src/frequenz/channels/_base_classes.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# License: MIT
22
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
33

4-
"""Baseclasses for Channel Sender and Receiver."""
4+
"""Base classes for Channel Sender and Receiver."""
55

66
from __future__ import annotations
77

@@ -169,11 +169,13 @@ def __init__(self, recv: Receiver[T], transform: Callable[[T], U]) -> None:
169169
170170
Args:
171171
recv: The input receiver.
172-
transform: The function to run on the input
173-
data.
172+
transform: The function to run on the input data.
174173
"""
175174
self._recv = recv
175+
"""The input receiver."""
176+
176177
self._transform = transform
178+
"""The function to run on the input data."""
177179

178180
async def ready(self) -> bool:
179181
"""Wait until the receiver is ready with a value or an error.

src/frequenz/channels/_bidirectional.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ def __init__(
3838
receiver: A receiver to receive values from.
3939
"""
4040
self._chan = channel
41+
"""The underlying channel."""
42+
4143
self._sender = sender
44+
"""The sender to send values with."""
45+
4246
self._receiver = receiver
47+
"""The receiver to receive values from."""
4348

4449
async def send(self, msg: V) -> None:
4550
"""Send a value to the other side.
@@ -57,7 +62,7 @@ async def send(self, msg: V) -> None:
5762
except SenderError as err:
5863
# If this comes from a channel error, then we inject another
5964
# ChannelError having the information about the Bidirectional
60-
# channel to hide (at least partially) the underlaying
65+
# channel to hide (at least partially) the underlying
6166
# Broadcast channels we use.
6267
if isinstance(err.__cause__, ChannelError):
6368
this_chan_error = ChannelError(
@@ -98,7 +103,7 @@ def consume(self) -> W:
98103
except ReceiverError as err:
99104
# If this comes from a channel error, then we inject another
100105
# ChannelError having the information about the Bidirectional
101-
# channel to hide (at least partially) the underlaying
106+
# channel to hide (at least partially) the underlying
102107
# Broadcast channels we use.
103108
if isinstance(err.__cause__, ChannelError):
104109
this_chan_error = ChannelError(
@@ -117,21 +122,29 @@ def __init__(self, client_id: str, service_id: str) -> None:
117122
service_id: A name for the service end of the channels.
118123
"""
119124
self._client_id = client_id
125+
"""The name for the client, used to name the channels."""
126+
120127
self._request_channel: Broadcast[T] = Broadcast(f"req_{service_id}_{client_id}")
128+
"""The channel to send requests."""
129+
121130
self._response_channel: Broadcast[U] = Broadcast(
122131
f"resp_{service_id}_{client_id}"
123132
)
133+
"""The channel to send responses."""
124134

125135
self._client_handle = Bidirectional.Handle(
126136
self,
127137
self._request_channel.new_sender(),
128138
self._response_channel.new_receiver(),
129139
)
140+
"""The handle for the client side to send/receive values."""
141+
130142
self._service_handle = Bidirectional.Handle(
131143
self,
132144
self._response_channel.new_sender(),
133145
self._request_channel.new_receiver(),
134146
)
147+
"""The handle for the service side to send/receive values."""
135148

136149
@property
137150
def client_handle(self) -> Bidirectional.Handle[T, U]:

src/frequenz/channels/_broadcast.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,25 @@ def __init__(self, name: str, resend_latest: bool = False) -> None:
8585
to wait for the next message on the channel to arrive.
8686
"""
8787
self.name: str = name
88+
"""The name of the broadcast channel.
89+
90+
Only used for debugging purposes.
91+
"""
92+
8893
self._resend_latest = resend_latest
94+
"""Whether to resend the latest value to new receivers."""
8995

9096
self.recv_cv: Condition = Condition()
97+
"""The condition to wait for data in the channel's buffer."""
98+
9199
self.receivers: dict[UUID, weakref.ReferenceType[Receiver[T]]] = {}
100+
"""The receivers attached to the channel, indexed by their UUID."""
101+
92102
self.closed: bool = False
103+
"""Whether the channel is closed."""
104+
93105
self._latest: T | None = None
106+
"""The latest value sent to the channel."""
94107

95108
async def close(self) -> None:
96109
"""Close the Broadcast channel.
@@ -167,6 +180,7 @@ def __init__(self, chan: Broadcast[T]) -> None:
167180
chan: A reference to the broadcast channel this sender belongs to.
168181
"""
169182
self._chan = chan
183+
"""The broadcast channel this sender belongs to."""
170184

171185
async def send(self, msg: T) -> None:
172186
"""Send a message to all broadcast receivers.
@@ -222,11 +236,26 @@ def __init__(self, uuid: UUID, name: str, maxsize: int, chan: Broadcast[T]) -> N
222236
belongs to.
223237
"""
224238
self._uuid = uuid
239+
"""The UUID to identify the receiver in the broadcast channel's list of receivers."""
240+
225241
self._name = name
242+
"""The name to identify the receiver.
243+
244+
Only used for debugging purposes.
245+
"""
246+
226247
self._chan = chan
248+
"""The broadcast channel that this receiver belongs to."""
249+
227250
self._q: Deque[T] = deque(maxlen=maxsize)
251+
"""The receiver's internal message queue."""
228252

229253
self._active = True
254+
"""Whether the receiver is still active.
255+
256+
If this receiver is converted into a Peekable, it will neither be
257+
considered valid nor active.
258+
"""
230259

231260
def enqueue(self, msg: T) -> None:
232261
"""Put a message into this receiver's queue.
@@ -311,7 +340,7 @@ def consume(self) -> T:
311340
if not self._q and self._chan.closed:
312341
raise ReceiverStoppedError(self) from ChannelClosedError(self._chan)
313342

314-
assert self._q, "`consume()` must be preceeded by a call to `ready()`"
343+
assert self._q, "`consume()` must be preceded by a call to `ready()`"
315344
return self._q.popleft()
316345

317346
def into_peekable(self) -> Peekable[T]:
@@ -343,6 +372,7 @@ def __init__(self, chan: Broadcast[T]) -> None:
343372
chan: The broadcast channel this Peekable will try to peek into.
344373
"""
345374
self._chan = chan
375+
"""The broadcast channel this Peekable will try to peek into."""
346376

347377
def peek(self) -> T | None:
348378
"""Return the latest value that was sent to the channel.

src/frequenz/channels/_exceptions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def __init__(self, message: Any, channel: Any):
4343
"""
4444
super().__init__(message)
4545
self.channel: Any = channel
46+
"""The channel where the error happened."""
4647

4748

4849
class ChannelClosedError(ChannelError):
@@ -73,6 +74,7 @@ def __init__(self, message: Any, sender: _base_classes.Sender[T]):
7374
"""
7475
super().__init__(message)
7576
self.sender: _base_classes.Sender[T] = sender
77+
"""The sender where the error happened."""
7678

7779

7880
class ReceiverError(Error, Generic[T]):
@@ -91,6 +93,7 @@ def __init__(self, message: Any, receiver: _base_classes.Receiver[T]):
9193
"""
9294
super().__init__(message)
9395
self.receiver: _base_classes.Receiver[T] = receiver
96+
"""The receiver where the error happened."""
9497

9598

9699
class ReceiverStoppedError(ReceiverError[T]):

src/frequenz/channels/util/_event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ async def exit_after_10_seconds() -> None:
4141
if selected_from(selected, other_receiver):
4242
print(selected.value)
4343
else:
44-
assert False, "Unknow receiver selected"
44+
assert False, "Unknown receiver selected"
4545
```
4646
"""
4747

src/frequenz/channels/util/_file_watcher.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# License: MIT
22
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
33

4-
"""A Channel receiver for watching for new (or modified) files."""
4+
"""A Channel receiver for watching for new, modified or deleted files."""
55

66
from __future__ import annotations
77

@@ -55,6 +55,8 @@ def __init__(
5555
all event types.
5656
"""
5757
self.event_types: frozenset[FileWatcher.EventType] = frozenset(event_types)
58+
"""The types of events to watch for."""
59+
5860
self._stop_event = asyncio.Event()
5961
self._paths = [
6062
path if isinstance(path, pathlib.Path) else pathlib.Path(path)
@@ -129,7 +131,7 @@ def consume(self) -> Event:
129131
if not self._changes and self._awatch_stopped_exc is not None:
130132
raise ReceiverStoppedError(self) from self._awatch_stopped_exc
131133

132-
assert self._changes, "`consume()` must be preceeded by a call to `ready()`"
134+
assert self._changes, "`consume()` must be preceded by a call to `ready()`"
133135
# Tuple of (Change, path) returned by watchfiles
134136
change, path_str = self._changes.pop()
135137
return FileWatcher.Event(

src/frequenz/channels/util/_merge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,6 @@ def consume(self) -> T:
113113
if not self._results and not self._pending:
114114
raise ReceiverStoppedError(self)
115115

116-
assert self._results, "`consume()` must be preceeded by a call to `ready()`"
116+
assert self._results, "`consume()` must be preceded by a call to `ready()`"
117117

118118
return self._results.popleft()

src/frequenz/channels/util/_merge_named.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ def __init__(self, **kwargs: Receiver[T]) -> None:
2525
**kwargs: sequence of channel receivers.
2626
"""
2727
self._receivers = kwargs
28+
"""The sequence of channel receivers to get the messages to merge."""
29+
2830
self._pending: set[asyncio.Task[Any]] = {
2931
asyncio.create_task(recv.__anext__(), name=name)
3032
for name, recv in self._receivers.items()
3133
}
34+
"""The set of pending tasks to merge messages."""
35+
3236
self._results: Deque[tuple[str, T]] = deque(maxlen=len(self._receivers))
37+
"""The internal buffer of merged messages."""
3338

3439
def __del__(self) -> None:
3540
"""Cleanup any pending tasks."""
@@ -94,6 +99,6 @@ def consume(self) -> tuple[str, T]:
9499
if not self._results and not self._pending:
95100
raise ReceiverStoppedError(self)
96101

97-
assert self._results, "`consume()` must be preceeded by a call to `ready()`"
102+
assert self._results, "`consume()` must be preceded by a call to `ready()`"
98103

99104
return self._results.popleft()

src/frequenz/channels/util/_select.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,14 @@ def __init__(self, selected: Selected[_T]) -> None:
192192
recv = selected._recv # pylint: disable=protected-access
193193
super().__init__(f"Selected receiver {recv} was not handled in the if-chain")
194194
self.selected = selected
195+
"""The selected receiver that was not handled."""
195196

196197

197198
class SelectErrorGroup(BaseExceptionGroup[BaseException], SelectError):
198199
"""An exception group for [`select()`][frequenz.channels.util.select] operation.
199200
200201
This exception group is raised when a `select()` loops fails while cleaning up
201-
runing tasts to check for ready receivers.
202+
running tests to check for ready receivers.
202203
"""
203204

204205

0 commit comments

Comments
 (0)