Skip to content

Commit e2c3873

Browse files
committed
Make merge and Merger covariant
When merging, we should allow containers of more specific types to be mixed with less specific types as long as the result of the merge uses the less specific type, so we use a covariant type variable for it. We also use the new public type variables. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 7a7fb37 commit e2c3873

File tree

1 file changed

+10
-9
lines changed

1 file changed

+10
-9
lines changed

src/frequenz/channels/_merge.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,13 @@
5252
import asyncio
5353
import itertools
5454
from collections import deque
55-
from typing import Any, TypeVar
55+
from typing import Any
5656

57+
from ._generic import ReceiverMessageT_co
5758
from ._receiver import Receiver, ReceiverStoppedError
5859

59-
_T = TypeVar("_T")
6060

61-
62-
def merge(*receivers: Receiver[_T]) -> Merger[_T]:
61+
def merge(*receivers: Receiver[ReceiverMessageT_co]) -> Merger[ReceiverMessageT_co]:
6362
"""Merge messages coming from multiple receivers into a single stream.
6463
6564
Example:
@@ -95,31 +94,33 @@ def merge(*receivers: Receiver[_T]) -> Merger[_T]:
9594
return Merger(*receivers, name="merge")
9695

9796

98-
class Merger(Receiver[_T]):
97+
class Merger(Receiver[ReceiverMessageT_co]):
9998
"""A receiver that merges messages coming from multiple receivers into a single stream.
10099
101100
Tip:
102101
Please consider using the more idiomatic [`merge()`][frequenz.channels.merge]
103102
function instead of creating a `Merger` instance directly.
104103
"""
105104

106-
def __init__(self, *receivers: Receiver[_T], name: str | None) -> None:
105+
def __init__(
106+
self, *receivers: Receiver[ReceiverMessageT_co], name: str | None
107+
) -> None:
107108
"""Initialize this merger.
108109
109110
Args:
110111
*receivers: The receivers to merge.
111112
name: The name of the receiver. Used to create the string representation
112113
of the receiver.
113114
"""
114-
self._receivers: dict[str, Receiver[_T]] = {
115+
self._receivers: dict[str, Receiver[ReceiverMessageT_co]] = {
115116
str(id): recv for id, recv in enumerate(receivers)
116117
}
117118
self._name: str = name if name is not None else type(self).__name__
118119
self._pending: set[asyncio.Task[Any]] = {
119120
asyncio.create_task(anext(recv), name=name)
120121
for name, recv in self._receivers.items()
121122
}
122-
self._results: deque[_T] = deque(maxlen=len(self._receivers))
123+
self._results: deque[ReceiverMessageT_co] = deque(maxlen=len(self._receivers))
123124

124125
def __del__(self) -> None:
125126
"""Finalize this merger."""
@@ -170,7 +171,7 @@ async def ready(self) -> bool:
170171
asyncio.create_task(anext(self._receivers[name]), name=name)
171172
)
172173

173-
def consume(self) -> _T:
174+
def consume(self) -> ReceiverMessageT_co:
174175
"""Return the latest message once `ready` is complete.
175176
176177
Returns:

0 commit comments

Comments
 (0)