Skip to content

Commit 4e7a466

Browse files
committed
open_memory_channel(): return a named tuple
partially addresses #719
1 parent 18a14bd commit 4e7a466

File tree

5 files changed

+30
-7
lines changed

5 files changed

+30
-7
lines changed

docs/source/reference-core.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,12 @@ inside a single process, and for that you can use
11241124

11251125
.. autofunction:: open_memory_channel(max_buffer_size)
11261126

1127+
Assigning the send and receive channels to separate variables usually
1128+
produces the most readable code. However, in situations where the pair
1129+
is preserved-- such as a collection of memory channels-- prefer named tuple
1130+
access (``pair.send_channel``, ``pair.receive_channel``) over indexed access
1131+
(``pair[0]``, ``pair[1]``).
1132+
11271133
.. note:: If you've used the :mod:`threading` or :mod:`asyncio`
11281134
modules, you may be familiar with :class:`queue.Queue` or
11291135
:class:`asyncio.Queue`. In Trio, :func:`open_memory_channel` is

newsfragments/1771.feature.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
open_memory_channel() now returns a named tuple with attributes ``send_channel``
2+
and ```receive_channel`. This can be used to avoid indexed access of the
3+
channel halves in some scenarios such as a collection of channels. (Note: when
4+
dealing with a single memory channel, assigning the send and receive halves
5+
to separate variables via destructuring is still considered more readable.)

trio/_channel.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
from collections import deque, OrderedDict
22
from math import inf
3+
from typing import NamedTuple
34

45
import attr
56
from outcome import Error, Value
67

7-
from .abc import SendChannel, ReceiveChannel, Channel
8+
from .abc import SendChannel, ReceiveChannel
89
from ._util import generic_function, NoPublicConstructor
910

1011
import trio
1112
from ._core import enable_ki_protection
1213

1314

15+
class MemoryChannelPair(NamedTuple):
16+
"""Named tuple of send/receive memory channels"""
17+
18+
send_channel: "MemorySendChannel"
19+
receive_channel: "MemoryReceiveChannel"
20+
21+
1422
@generic_function
1523
def open_memory_channel(max_buffer_size):
1624
"""Open a channel for passing objects between tasks within a process.
@@ -40,9 +48,8 @@ def open_memory_channel(max_buffer_size):
4048
see :ref:`channel-buffering` for more details. If in doubt, use 0.
4149
4250
Returns:
43-
A pair ``(send_channel, receive_channel)``. If you have
44-
trouble remembering which order these go in, remember: data
45-
flows from left → right.
51+
A named tuple ``(send_channel, receive_channel)``. The tuple ordering is
52+
intended to match the image of data flowing from left → right.
4653
4754
In addition to the standard channel methods, all memory channel objects
4855
provide a ``statistics()`` method, which returns an object with the
@@ -69,7 +76,7 @@ def open_memory_channel(max_buffer_size):
6976
if max_buffer_size < 0:
7077
raise ValueError("max_buffer_size must be >= 0")
7178
state = MemoryChannelState(max_buffer_size)
72-
return (
79+
return MemoryChannelPair(
7380
MemorySendChannel._create(state),
7481
MemoryReceiveChannel._create(state),
7582
)

trio/tests/test_channel.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,3 +350,8 @@ async def do_send(s, v):
350350
assert await r.receive() == 1
351351
with pytest.raises(trio.WouldBlock):
352352
r.receive_nowait()
353+
354+
355+
def test_named_tuple():
356+
pair = open_memory_channel(0)
357+
assert pair.send_channel, pair.receive_channel == pair

trio/tests/test_highlevel_serve_listeners.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ class MemoryListener(trio.abc.Listener):
1919
async def connect(self):
2020
assert not self.closed
2121
client, server = memory_stream_pair()
22-
await self.queued_streams[0].send(server)
22+
await self.queued_streams.send_channel.send(server)
2323
return client
2424

2525
async def accept(self):
2626
await trio.lowlevel.checkpoint()
2727
assert not self.closed
2828
if self.accept_hook is not None:
2929
await self.accept_hook()
30-
stream = await self.queued_streams[1].receive()
30+
stream = await self.queued_streams.receive_channel.receive()
3131
self.accepted_streams.append(stream)
3232
return stream
3333

0 commit comments

Comments
 (0)