Skip to content

Commit

Permalink
open_memory_channel(): return a named tuple
Browse files Browse the repository at this point in the history
partially addresses python-trio#719
  • Loading branch information
belm0 committed Oct 26, 2020
1 parent 18a14bd commit d4a8db0
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 7 deletions.
6 changes: 6 additions & 0 deletions docs/source/reference-core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,12 @@ inside a single process, and for that you can use

.. autofunction:: open_memory_channel(max_buffer_size)

Assigning the send and receive channels to separate variables usually
produces the most readable code. However, in situations where the pair
is preserved-- such as a collection of memory channels-- prefer named tuple
access (``pair.send_channel``, ``pair.receive_channel``) over indexed access
(``pair[0]``, ``pair[1]``).

.. note:: If you've used the :mod:`threading` or :mod:`asyncio`
modules, you may be familiar with :class:`queue.Queue` or
:class:`asyncio.Queue`. In Trio, :func:`open_memory_channel` is
Expand Down
5 changes: 5 additions & 0 deletions newsfragments/1771.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
open_memory_channel() now returns a named tuple with attributes `send_channel`
and `receive_channel`. This can be used to avoid indexed access of the channel
halves in some scenarios such as a collection of channels. (Note: when
dealing with a single memory channel, assigning the send and receive halves
to separate variables via destructuring is still considered more readable.)
17 changes: 12 additions & 5 deletions trio/_channel.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
from collections import deque, OrderedDict
from math import inf
from typing import NamedTuple

import attr
from outcome import Error, Value

from .abc import SendChannel, ReceiveChannel, Channel
from .abc import SendChannel, ReceiveChannel
from ._util import generic_function, NoPublicConstructor

import trio
from ._core import enable_ki_protection


class MemoryChannelPair(NamedTuple):
"""Named tuple of send/receive memory channels"""

send_channel: "MemorySendChannel"
receive_channel: "MemoryReceiveChannel"


@generic_function
def open_memory_channel(max_buffer_size):
"""Open a channel for passing objects between tasks within a process.
Expand Down Expand Up @@ -40,9 +48,8 @@ def open_memory_channel(max_buffer_size):
see :ref:`channel-buffering` for more details. If in doubt, use 0.
Returns:
A pair ``(send_channel, receive_channel)``. If you have
trouble remembering which order these go in, remember: data
flows from left → right.
A named tuple ``(send_channel, receive_channel)``. The tuple ordering is
intended to match the image of data flowing from left → right.
In addition to the standard channel methods, all memory channel objects
provide a ``statistics()`` method, which returns an object with the
Expand All @@ -69,7 +76,7 @@ def open_memory_channel(max_buffer_size):
if max_buffer_size < 0:
raise ValueError("max_buffer_size must be >= 0")
state = MemoryChannelState(max_buffer_size)
return (
return MemoryChannelPair(
MemorySendChannel._create(state),
MemoryReceiveChannel._create(state),
)
Expand Down
5 changes: 5 additions & 0 deletions trio/tests/test_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,3 +350,8 @@ async def do_send(s, v):
assert await r.receive() == 1
with pytest.raises(trio.WouldBlock):
r.receive_nowait()


def test_named_tuple():
pair = open_memory_channel(0)
assert pair.send_channel, pair.receive_channel == pair
4 changes: 2 additions & 2 deletions trio/tests/test_highlevel_serve_listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ class MemoryListener(trio.abc.Listener):
async def connect(self):
assert not self.closed
client, server = memory_stream_pair()
await self.queued_streams[0].send(server)
await self.queued_streams.send_channel.send(server)
return client

async def accept(self):
await trio.lowlevel.checkpoint()
assert not self.closed
if self.accept_hook is not None:
await self.accept_hook()
stream = await self.queued_streams[1].receive()
stream = await self.queued_streams.receive_channel.receive()
self.accepted_streams.append(stream)
return stream

Expand Down

0 comments on commit d4a8db0

Please sign in to comment.