Skip to content

Commit 3b28017

Browse files
committed
RFC: Revamp the repository modules structure
These are the most notable changes: - The `_base_classes` modules is split into the `_receiver` and `_sender` modules. - Sender and receiver exceptions are moved from the `_exceptions` module to the new `_sender` and `_receiver` modules. - The `frequenz.channel` package now only exposes the new `_receiver` and `_sender` modules, and the `_exceptions` and `_select` modules. - All channels and receiver modules are moved to the `frequenz.channels` package and made public. - All public nested classes were moved to the top level of the corresponding module. Advantages of this structure: - It completely removes circular dependencies. - It avoids importing unnecessary code. In Python importing means code execution, so even when it is done only at startup, it adds some overhead. Also by not importing unnecessary code, we can potentially add real optional dependencies. For example, if a project doesn't need to use a file watcher, they could avoid pulling the unnecessary `awatch` dependency. This is not done in this PR, but it could be done in the future. - By having the channels and receivers on their own module we can move public nested classes were moved to the top level of the corresponding module withough having to add superflous prefixes for support classes. - Removing nested classes avoids having to use hacky constructions, like requiring the use of `from __future__ import annotations`, types as strings (nested classes) and confusing the `mkdocstrings` tools when extracting and cross-linking docs. - The `frequenz.channels` package exposes all classes that are used once you have setted up your channels, so the importing should still be pretty terse in most cases and only `frequenz.channels` would need to be imported in modules only taking some receivers and iterating or selecting over them. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent a698896 commit 3b28017

29 files changed

+494
-553
lines changed

benchmarks/benchmark_anycast.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from collections.abc import Coroutine
1010
from typing import Any
1111

12-
from frequenz.channels import Anycast, Receiver, Sender
12+
from frequenz.channels import Receiver, Sender
13+
from frequenz.channels.anycast import Anycast
1314

1415

1516
async def send_msg(num_messages: int, chan: Sender[int]) -> None:

benchmarks/benchmark_broadcast.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from functools import partial
1111
from typing import Any
1212

13-
from frequenz.channels import Broadcast, Receiver, Sender
13+
from frequenz.channels import Receiver, Sender
14+
from frequenz.channels.broadcast import Broadcast
1415

1516

1617
async def component_sender(num_messages: int, chan: Sender[int]) -> None:

src/frequenz/channels/__init__.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,25 @@
6161
a peekable.
6262
"""
6363

64-
from . import util
65-
from ._anycast import Anycast
66-
from ._base_classes import Peekable, Receiver, Sender
67-
from ._bidirectional import Bidirectional
68-
from ._broadcast import Broadcast
69-
from ._exceptions import (
70-
ChannelClosedError,
71-
ChannelError,
72-
Error,
64+
from ._exceptions import ChannelClosedError, ChannelError, Error
65+
from ._receiver import (
66+
Peekable,
67+
Receiver,
7368
ReceiverError,
7469
ReceiverInvalidatedError,
7570
ReceiverStoppedError,
76-
SenderError,
7771
)
72+
from ._select import (
73+
Selected,
74+
SelectError,
75+
SelectErrorGroup,
76+
UnhandledSelectedError,
77+
select,
78+
selected_from,
79+
)
80+
from ._sender import Sender, SenderError
7881

7982
__all__ = [
80-
"Anycast",
81-
"Bidirectional",
82-
"Broadcast",
8383
"ChannelClosedError",
8484
"ChannelError",
8585
"Error",
@@ -88,7 +88,12 @@
8888
"ReceiverError",
8989
"ReceiverInvalidatedError",
9090
"ReceiverStoppedError",
91+
"SelectError",
92+
"SelectErrorGroup",
93+
"Selected",
9194
"Sender",
9295
"SenderError",
93-
"util",
96+
"UnhandledSelectedError",
97+
"select",
98+
"selected_from",
9499
]

src/frequenz/channels/_bidirectional.py

Lines changed: 0 additions & 205 deletions
This file was deleted.

src/frequenz/channels/_exceptions.py

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,7 @@
33

44
"""Exception classes."""
55

6-
from __future__ import annotations
7-
8-
from typing import TYPE_CHECKING, Any, Generic, TypeVar
9-
10-
if TYPE_CHECKING:
11-
from . import _base_classes
12-
13-
T = TypeVar("T")
6+
from typing import Any
147

158

169
class Error(RuntimeError):
@@ -56,63 +49,3 @@ def __init__(self, channel: Any):
5649
channel: A reference to the channel that was closed.
5750
"""
5851
super().__init__(f"Channel {channel} was closed", channel)
59-
60-
61-
class SenderError(Error, Generic[T]):
62-
"""An error produced in a [Sender][frequenz.channels.Sender].
63-
64-
All exceptions generated by senders inherit from this exception.
65-
"""
66-
67-
def __init__(self, message: str, sender: _base_classes.Sender[T]):
68-
"""Create an instance.
69-
70-
Args:
71-
message: An error message.
72-
sender: The [Sender][frequenz.channels.Sender] where the error
73-
happened.
74-
"""
75-
super().__init__(message)
76-
self.sender: _base_classes.Sender[T] = sender
77-
"""The sender where the error happened."""
78-
79-
80-
class ReceiverError(Error, Generic[T]):
81-
"""An error produced in a [Receiver][frequenz.channels.Receiver].
82-
83-
All exceptions generated by receivers inherit from this exception.
84-
"""
85-
86-
def __init__(self, message: str, receiver: _base_classes.Receiver[T]):
87-
"""Create an instance.
88-
89-
Args:
90-
message: An error message.
91-
receiver: The [Receiver][frequenz.channels.Receiver] where the
92-
error happened.
93-
"""
94-
super().__init__(message)
95-
self.receiver: _base_classes.Receiver[T] = receiver
96-
"""The receiver where the error happened."""
97-
98-
99-
class ReceiverStoppedError(ReceiverError[T]):
100-
"""The [Receiver][frequenz.channels.Receiver] stopped producing messages."""
101-
102-
def __init__(self, receiver: _base_classes.Receiver[T]):
103-
"""Create an instance.
104-
105-
Args:
106-
receiver: The [Receiver][frequenz.channels.Receiver] where the
107-
error happened.
108-
"""
109-
super().__init__(f"Receiver {receiver} was stopped", receiver)
110-
111-
112-
class ReceiverInvalidatedError(ReceiverError[T]):
113-
"""The [Receiver][frequenz.channels.Receiver] was invalidated.
114-
115-
This happens when the Receiver is converted
116-
[into][frequenz.channels.Receiver.into_peekable]
117-
a [Peekable][frequenz.channels.Peekable].
118-
"""

0 commit comments

Comments
 (0)