703 lines
25 KiB
Python
703 lines
25 KiB
Python
from __future__ import annotations
|
||
|
||
import socket
|
||
from abc import ABC, abstractmethod
|
||
from typing import TYPE_CHECKING, Generic, TypeVar
|
||
|
||
import trio
|
||
|
||
if TYPE_CHECKING:
|
||
from types import TracebackType
|
||
|
||
from typing_extensions import Self
|
||
|
||
# both of these introduce circular imports if outside a TYPE_CHECKING guard
|
||
from ._socket import SocketType
|
||
from .lowlevel import Task
|
||
|
||
|
||
class Clock(ABC):
|
||
"""The interface for custom run loop clocks."""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
def start_clock(self) -> None:
|
||
"""Do any setup this clock might need.
|
||
|
||
Called at the beginning of the run.
|
||
|
||
"""
|
||
|
||
@abstractmethod
|
||
def current_time(self) -> float:
|
||
"""Return the current time, according to this clock.
|
||
|
||
This is used to implement functions like :func:`trio.current_time` and
|
||
:func:`trio.move_on_after`.
|
||
|
||
Returns:
|
||
float: The current time.
|
||
|
||
"""
|
||
|
||
@abstractmethod
|
||
def deadline_to_sleep_time(self, deadline: float) -> float:
|
||
"""Compute the real time until the given deadline.
|
||
|
||
This is called before we enter a system-specific wait function like
|
||
:func:`select.select`, to get the timeout to pass.
|
||
|
||
For a clock using wall-time, this should be something like::
|
||
|
||
return deadline - self.current_time()
|
||
|
||
but of course it may be different if you're implementing some kind of
|
||
virtual clock.
|
||
|
||
Args:
|
||
deadline (float): The absolute time of the next deadline,
|
||
according to this clock.
|
||
|
||
Returns:
|
||
float: The number of real seconds to sleep until the given
|
||
deadline. May be :data:`math.inf`.
|
||
|
||
"""
|
||
|
||
|
||
class Instrument(ABC): # noqa: B024 # conceptually is ABC
|
||
"""The interface for run loop instrumentation.
|
||
|
||
Instruments don't have to inherit from this abstract base class, and all
|
||
of these methods are optional. This class serves mostly as documentation.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
def before_run(self) -> None:
|
||
"""Called at the beginning of :func:`trio.run`."""
|
||
return
|
||
|
||
def after_run(self) -> None:
|
||
"""Called just before :func:`trio.run` returns."""
|
||
return
|
||
|
||
def task_spawned(self, task: Task) -> None:
|
||
"""Called when the given task is created.
|
||
|
||
Args:
|
||
task (trio.lowlevel.Task): The new task.
|
||
|
||
"""
|
||
return
|
||
|
||
def task_scheduled(self, task: Task) -> None:
|
||
"""Called when the given task becomes runnable.
|
||
|
||
It may still be some time before it actually runs, if there are other
|
||
runnable tasks ahead of it.
|
||
|
||
Args:
|
||
task (trio.lowlevel.Task): The task that became runnable.
|
||
|
||
"""
|
||
return
|
||
|
||
def before_task_step(self, task: Task) -> None:
|
||
"""Called immediately before we resume running the given task.
|
||
|
||
Args:
|
||
task (trio.lowlevel.Task): The task that is about to run.
|
||
|
||
"""
|
||
return
|
||
|
||
def after_task_step(self, task: Task) -> None:
|
||
"""Called when we return to the main run loop after a task has yielded.
|
||
|
||
Args:
|
||
task (trio.lowlevel.Task): The task that just ran.
|
||
|
||
"""
|
||
return
|
||
|
||
def task_exited(self, task: Task) -> None:
|
||
"""Called when the given task exits.
|
||
|
||
Args:
|
||
task (trio.lowlevel.Task): The finished task.
|
||
|
||
"""
|
||
return
|
||
|
||
def before_io_wait(self, timeout: float) -> None:
|
||
"""Called before blocking to wait for I/O readiness.
|
||
|
||
Args:
|
||
timeout (float): The number of seconds we are willing to wait.
|
||
|
||
"""
|
||
return
|
||
|
||
def after_io_wait(self, timeout: float) -> None:
|
||
"""Called after handling pending I/O.
|
||
|
||
Args:
|
||
timeout (float): The number of seconds we were willing to
|
||
wait. This much time may or may not have elapsed, depending on
|
||
whether any I/O was ready.
|
||
|
||
"""
|
||
return
|
||
|
||
|
||
class HostnameResolver(ABC):
|
||
"""If you have a custom hostname resolver, then implementing
|
||
:class:`HostnameResolver` allows you to register this to be used by Trio.
|
||
|
||
See :func:`trio.socket.set_custom_hostname_resolver`.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def getaddrinfo(
|
||
self,
|
||
host: bytes | None,
|
||
port: bytes | str | int | None,
|
||
family: int = 0,
|
||
type: int = 0,
|
||
proto: int = 0,
|
||
flags: int = 0,
|
||
) -> list[
|
||
tuple[
|
||
socket.AddressFamily,
|
||
socket.SocketKind,
|
||
int,
|
||
str,
|
||
tuple[str, int] | tuple[str, int, int, int],
|
||
]
|
||
]:
|
||
"""A custom implementation of :func:`~trio.socket.getaddrinfo`.
|
||
|
||
Called by :func:`trio.socket.getaddrinfo`.
|
||
|
||
If ``host`` is given as a numeric IP address, then
|
||
:func:`~trio.socket.getaddrinfo` may handle the request itself rather
|
||
than calling this method.
|
||
|
||
Any required IDNA encoding is handled before calling this function;
|
||
your implementation can assume that it will never see U-labels like
|
||
``"café.com"``, and only needs to handle A-labels like
|
||
``b"xn--caf-dma.com"``.
|
||
|
||
"""
|
||
|
||
@abstractmethod
|
||
async def getnameinfo(
|
||
self, sockaddr: tuple[str, int] | tuple[str, int, int, int], flags: int
|
||
) -> tuple[str, str]:
|
||
"""A custom implementation of :func:`~trio.socket.getnameinfo`.
|
||
|
||
Called by :func:`trio.socket.getnameinfo`.
|
||
|
||
"""
|
||
|
||
|
||
class SocketFactory(ABC):
|
||
"""If you write a custom class implementing the Trio socket interface,
|
||
then you can use a :class:`SocketFactory` to get Trio to use it.
|
||
|
||
See :func:`trio.socket.set_custom_socket_factory`.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
def socket(
|
||
self,
|
||
family: socket.AddressFamily | int = socket.AF_INET,
|
||
type: socket.SocketKind | int = socket.SOCK_STREAM,
|
||
proto: int = 0,
|
||
) -> SocketType:
|
||
"""Create and return a socket object.
|
||
|
||
Your socket object must inherit from :class:`trio.socket.SocketType`,
|
||
which is an empty class whose only purpose is to "mark" which classes
|
||
should be considered valid Trio sockets.
|
||
|
||
Called by :func:`trio.socket.socket`.
|
||
|
||
Note that unlike :func:`trio.socket.socket`, this does not take a
|
||
``fileno=`` argument. If a ``fileno=`` is specified, then
|
||
:func:`trio.socket.socket` returns a regular Trio socket object
|
||
instead of calling this method.
|
||
|
||
"""
|
||
|
||
|
||
class AsyncResource(ABC):
|
||
"""A standard interface for resources that needs to be cleaned up, and
|
||
where that cleanup may require blocking operations.
|
||
|
||
This class distinguishes between "graceful" closes, which may perform I/O
|
||
and thus block, and a "forceful" close, which cannot. For example, cleanly
|
||
shutting down a TLS-encrypted connection requires sending a "goodbye"
|
||
message; but if a peer has become non-responsive, then sending this
|
||
message might block forever, so we may want to just drop the connection
|
||
instead. Therefore the :meth:`aclose` method is unusual in that it
|
||
should always close the connection (or at least make its best attempt)
|
||
*even if it fails*; failure indicates a failure to achieve grace, not a
|
||
failure to close the connection.
|
||
|
||
Objects that implement this interface can be used as async context
|
||
managers, i.e., you can write::
|
||
|
||
async with create_resource() as some_async_resource:
|
||
...
|
||
|
||
Entering the context manager is synchronous (not a checkpoint); exiting it
|
||
calls :meth:`aclose`. The default implementations of
|
||
``__aenter__`` and ``__aexit__`` should be adequate for all subclasses.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def aclose(self) -> None:
|
||
"""Close this resource, possibly blocking.
|
||
|
||
IMPORTANT: This method may block in order to perform a "graceful"
|
||
shutdown. But, if this fails, then it still *must* close any
|
||
underlying resources before returning. An error from this method
|
||
indicates a failure to achieve grace, *not* a failure to close the
|
||
connection.
|
||
|
||
For example, suppose we call :meth:`aclose` on a TLS-encrypted
|
||
connection. This requires sending a "goodbye" message; but if the peer
|
||
has become non-responsive, then our attempt to send this message might
|
||
block forever, and eventually time out and be cancelled. In this case
|
||
the :meth:`aclose` method on :class:`~trio.SSLStream` will
|
||
immediately close the underlying transport stream using
|
||
:func:`trio.aclose_forcefully` before raising :exc:`~trio.Cancelled`.
|
||
|
||
If the resource is already closed, then this method should silently
|
||
succeed.
|
||
|
||
Once this method completes, any other pending or future operations on
|
||
this resource should generally raise :exc:`~trio.ClosedResourceError`,
|
||
unless there's a good reason to do otherwise.
|
||
|
||
See also: :func:`trio.aclose_forcefully`.
|
||
|
||
"""
|
||
|
||
async def __aenter__(self) -> Self:
|
||
return self
|
||
|
||
async def __aexit__(
|
||
self,
|
||
exc_type: type[BaseException] | None,
|
||
exc_value: BaseException | None,
|
||
traceback: TracebackType | None,
|
||
) -> None:
|
||
await self.aclose()
|
||
|
||
|
||
class SendStream(AsyncResource):
|
||
"""A standard interface for sending data on a byte stream.
|
||
|
||
The underlying stream may be unidirectional, or bidirectional. If it's
|
||
bidirectional, then you probably want to also implement
|
||
:class:`ReceiveStream`, which makes your object a :class:`Stream`.
|
||
|
||
:class:`SendStream` objects also implement the :class:`AsyncResource`
|
||
interface, so they can be closed by calling :meth:`~AsyncResource.aclose`
|
||
or using an ``async with`` block.
|
||
|
||
If you want to send Python objects rather than raw bytes, see
|
||
:class:`SendChannel`.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def send_all(self, data: bytes | bytearray | memoryview) -> None:
|
||
"""Sends the given data through the stream, blocking if necessary.
|
||
|
||
Args:
|
||
data (bytes, bytearray, or memoryview): The data to send.
|
||
|
||
Raises:
|
||
trio.BusyResourceError: if another task is already executing a
|
||
:meth:`send_all`, :meth:`wait_send_all_might_not_block`, or
|
||
:meth:`HalfCloseableStream.send_eof` on this stream.
|
||
trio.BrokenResourceError: if something has gone wrong, and the stream
|
||
is broken.
|
||
trio.ClosedResourceError: if you previously closed this stream
|
||
object, or if another task closes this stream object while
|
||
:meth:`send_all` is running.
|
||
|
||
Most low-level operations in Trio provide a guarantee: if they raise
|
||
:exc:`trio.Cancelled`, this means that they had no effect, so the
|
||
system remains in a known state. This is **not true** for
|
||
:meth:`send_all`. If this operation raises :exc:`trio.Cancelled` (or
|
||
any other exception for that matter), then it may have sent some, all,
|
||
or none of the requested data, and there is no way to know which.
|
||
|
||
"""
|
||
|
||
@abstractmethod
|
||
async def wait_send_all_might_not_block(self) -> None:
|
||
"""Block until it's possible that :meth:`send_all` might not block.
|
||
|
||
This method may return early: it's possible that after it returns,
|
||
:meth:`send_all` will still block. (In the worst case, if no better
|
||
implementation is available, then it might always return immediately
|
||
without blocking. It's nice to do better than that when possible,
|
||
though.)
|
||
|
||
This method **must not** return *late*: if it's possible for
|
||
:meth:`send_all` to complete without blocking, then it must
|
||
return. When implementing it, err on the side of returning early.
|
||
|
||
Raises:
|
||
trio.BusyResourceError: if another task is already executing a
|
||
:meth:`send_all`, :meth:`wait_send_all_might_not_block`, or
|
||
:meth:`HalfCloseableStream.send_eof` on this stream.
|
||
trio.BrokenResourceError: if something has gone wrong, and the stream
|
||
is broken.
|
||
trio.ClosedResourceError: if you previously closed this stream
|
||
object, or if another task closes this stream object while
|
||
:meth:`wait_send_all_might_not_block` is running.
|
||
|
||
Note:
|
||
|
||
This method is intended to aid in implementing protocols that want
|
||
to delay choosing which data to send until the last moment. E.g.,
|
||
suppose you're working on an implementation of a remote display server
|
||
like `VNC
|
||
<https://en.wikipedia.org/wiki/Virtual_Network_Computing>`__, and
|
||
the network connection is currently backed up so that if you call
|
||
:meth:`send_all` now then it will sit for 0.5 seconds before actually
|
||
sending anything. In this case it doesn't make sense to take a
|
||
screenshot, then wait 0.5 seconds, and then send it, because the
|
||
screen will keep changing while you wait; it's better to wait 0.5
|
||
seconds, then take the screenshot, and then send it, because this
|
||
way the data you deliver will be more
|
||
up-to-date. Using :meth:`wait_send_all_might_not_block` makes it
|
||
possible to implement the better strategy.
|
||
|
||
If you use this method, you might also want to read up on
|
||
``TCP_NOTSENT_LOWAT``.
|
||
|
||
Further reading:
|
||
|
||
* `Prioritization Only Works When There's Pending Data to Prioritize
|
||
<https://insouciant.org/tech/prioritization-only-works-when-theres-pending-data-to-prioritize/>`__
|
||
|
||
* WWDC 2015: Your App and Next Generation Networks: `slides
|
||
<http://devstreaming.apple.com/videos/wwdc/2015/719ui2k57m/719/719_your_app_and_next_generation_networks.pdf?dl=1>`__,
|
||
`video and transcript
|
||
<https://developer.apple.com/videos/play/wwdc2015/719/>`__
|
||
|
||
"""
|
||
|
||
|
||
class ReceiveStream(AsyncResource):
|
||
"""A standard interface for receiving data on a byte stream.
|
||
|
||
The underlying stream may be unidirectional, or bidirectional. If it's
|
||
bidirectional, then you probably want to also implement
|
||
:class:`SendStream`, which makes your object a :class:`Stream`.
|
||
|
||
:class:`ReceiveStream` objects also implement the :class:`AsyncResource`
|
||
interface, so they can be closed by calling :meth:`~AsyncResource.aclose`
|
||
or using an ``async with`` block.
|
||
|
||
If you want to receive Python objects rather than raw bytes, see
|
||
:class:`ReceiveChannel`.
|
||
|
||
`ReceiveStream` objects can be used in ``async for`` loops. Each iteration
|
||
will produce an arbitrary sized chunk of bytes, like calling
|
||
`receive_some` with no arguments. Every chunk will contain at least one
|
||
byte, and the loop automatically exits when reaching end-of-file.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:
|
||
"""Wait until there is data available on this stream, and then return
|
||
some of it.
|
||
|
||
A return value of ``b""`` (an empty bytestring) indicates that the
|
||
stream has reached end-of-file. Implementations should be careful that
|
||
they return ``b""`` if, and only if, the stream has reached
|
||
end-of-file!
|
||
|
||
Args:
|
||
max_bytes (int): The maximum number of bytes to return. Must be
|
||
greater than zero. Optional; if omitted, then the stream object
|
||
is free to pick a reasonable default.
|
||
|
||
Returns:
|
||
bytes or bytearray: The data received.
|
||
|
||
Raises:
|
||
trio.BusyResourceError: if two tasks attempt to call
|
||
:meth:`receive_some` on the same stream at the same time.
|
||
trio.BrokenResourceError: if something has gone wrong, and the stream
|
||
is broken.
|
||
trio.ClosedResourceError: if you previously closed this stream
|
||
object, or if another task closes this stream object while
|
||
:meth:`receive_some` is running.
|
||
|
||
"""
|
||
|
||
def __aiter__(self) -> Self:
|
||
return self
|
||
|
||
async def __anext__(self) -> bytes | bytearray:
|
||
data = await self.receive_some()
|
||
if not data:
|
||
raise StopAsyncIteration
|
||
return data
|
||
|
||
|
||
class Stream(SendStream, ReceiveStream):
|
||
"""A standard interface for interacting with bidirectional byte streams.
|
||
|
||
A :class:`Stream` is an object that implements both the
|
||
:class:`SendStream` and :class:`ReceiveStream` interfaces.
|
||
|
||
If implementing this interface, you should consider whether you can go one
|
||
step further and implement :class:`HalfCloseableStream`.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
|
||
class HalfCloseableStream(Stream):
|
||
"""This interface extends :class:`Stream` to also allow closing the send
|
||
part of the stream without closing the receive part.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def send_eof(self) -> None:
|
||
"""Send an end-of-file indication on this stream, if possible.
|
||
|
||
The difference between :meth:`send_eof` and
|
||
:meth:`~AsyncResource.aclose` is that :meth:`send_eof` is a
|
||
*unidirectional* end-of-file indication. After you call this method,
|
||
you shouldn't try sending any more data on this stream, and your
|
||
remote peer should receive an end-of-file indication (eventually,
|
||
after receiving all the data you sent before that). But, they may
|
||
continue to send data to you, and you can continue to receive it by
|
||
calling :meth:`~ReceiveStream.receive_some`. You can think of it as
|
||
calling :meth:`~AsyncResource.aclose` on just the
|
||
:class:`SendStream` "half" of the stream object (and in fact that's
|
||
literally how :class:`trio.StapledStream` implements it).
|
||
|
||
Examples:
|
||
|
||
* On a socket, this corresponds to ``shutdown(..., SHUT_WR)`` (`man
|
||
page <https://linux.die.net/man/2/shutdown>`__).
|
||
|
||
* The SSH protocol provides the ability to multiplex bidirectional
|
||
"channels" on top of a single encrypted connection. A Trio
|
||
implementation of SSH could expose these channels as
|
||
:class:`HalfCloseableStream` objects, and calling :meth:`send_eof`
|
||
would send an ``SSH_MSG_CHANNEL_EOF`` request (see `RFC 4254 §5.3
|
||
<https://tools.ietf.org/html/rfc4254#section-5.3>`__).
|
||
|
||
* On an SSL/TLS-encrypted connection, the protocol doesn't provide any
|
||
way to do a unidirectional shutdown without closing the connection
|
||
entirely, so :class:`~trio.SSLStream` implements
|
||
:class:`Stream`, not :class:`HalfCloseableStream`.
|
||
|
||
If an EOF has already been sent, then this method should silently
|
||
succeed.
|
||
|
||
Raises:
|
||
trio.BusyResourceError: if another task is already executing a
|
||
:meth:`~SendStream.send_all`,
|
||
:meth:`~SendStream.wait_send_all_might_not_block`, or
|
||
:meth:`send_eof` on this stream.
|
||
trio.BrokenResourceError: if something has gone wrong, and the stream
|
||
is broken.
|
||
trio.ClosedResourceError: if you previously closed this stream
|
||
object, or if another task closes this stream object while
|
||
:meth:`send_eof` is running.
|
||
|
||
"""
|
||
|
||
|
||
# A regular invariant generic type
|
||
T = TypeVar("T")
|
||
|
||
# The type of object produced by a ReceiveChannel (covariant because
|
||
# ReceiveChannel[Derived] can be passed to someone expecting
|
||
# ReceiveChannel[Base])
|
||
ReceiveType = TypeVar("ReceiveType", covariant=True)
|
||
|
||
# The type of object accepted by a SendChannel (contravariant because
|
||
# SendChannel[Base] can be passed to someone expecting
|
||
# SendChannel[Derived])
|
||
SendType = TypeVar("SendType", contravariant=True)
|
||
|
||
# The type of object produced by a Listener (covariant plus must be
|
||
# an AsyncResource)
|
||
T_resource = TypeVar("T_resource", bound=AsyncResource, covariant=True)
|
||
|
||
|
||
class Listener(AsyncResource, Generic[T_resource]):
|
||
"""A standard interface for listening for incoming connections.
|
||
|
||
:class:`Listener` objects also implement the :class:`AsyncResource`
|
||
interface, so they can be closed by calling :meth:`~AsyncResource.aclose`
|
||
or using an ``async with`` block.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def accept(self) -> T_resource:
|
||
"""Wait until an incoming connection arrives, and then return it.
|
||
|
||
Returns:
|
||
AsyncResource: An object representing the incoming connection. In
|
||
practice this is generally some kind of :class:`Stream`,
|
||
but in principle you could also define a :class:`Listener` that
|
||
returned, say, channel objects.
|
||
|
||
Raises:
|
||
trio.BusyResourceError: if two tasks attempt to call
|
||
:meth:`accept` on the same listener at the same time.
|
||
trio.ClosedResourceError: if you previously closed this listener
|
||
object, or if another task closes this listener object while
|
||
:meth:`accept` is running.
|
||
|
||
Listeners don't generally raise :exc:`~trio.BrokenResourceError`,
|
||
because for listeners there is no general condition of "the
|
||
network/remote peer broke the connection" that can be handled in a
|
||
generic way, like there is for streams. Other errors *can* occur and
|
||
be raised from :meth:`accept` – for example, if you run out of file
|
||
descriptors then you might get an :class:`OSError` with its errno set
|
||
to ``EMFILE``.
|
||
|
||
"""
|
||
|
||
|
||
class SendChannel(AsyncResource, Generic[SendType]):
|
||
"""A standard interface for sending Python objects to some receiver.
|
||
|
||
`SendChannel` objects also implement the `AsyncResource` interface, so
|
||
they can be closed by calling `~AsyncResource.aclose` or using an ``async
|
||
with`` block.
|
||
|
||
If you want to send raw bytes rather than Python objects, see
|
||
`SendStream`.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def send(self, value: SendType) -> None:
|
||
"""Attempt to send an object through the channel, blocking if necessary.
|
||
|
||
Args:
|
||
value (object): The object to send.
|
||
|
||
Raises:
|
||
trio.BrokenResourceError: if something has gone wrong, and the
|
||
channel is broken. For example, you may get this if the receiver
|
||
has already been closed.
|
||
trio.ClosedResourceError: if you previously closed this
|
||
:class:`SendChannel` object, or if another task closes it while
|
||
:meth:`send` is running.
|
||
trio.BusyResourceError: some channels allow multiple tasks to call
|
||
`send` at the same time, but others don't. If you try to call
|
||
`send` simultaneously from multiple tasks on a channel that
|
||
doesn't support it, then you can get `~trio.BusyResourceError`.
|
||
|
||
"""
|
||
|
||
|
||
class ReceiveChannel(AsyncResource, Generic[ReceiveType]):
|
||
"""A standard interface for receiving Python objects from some sender.
|
||
|
||
You can iterate over a :class:`ReceiveChannel` using an ``async for``
|
||
loop::
|
||
|
||
async for value in receive_channel:
|
||
...
|
||
|
||
This is equivalent to calling :meth:`receive` repeatedly. The loop exits
|
||
without error when `receive` raises `~trio.EndOfChannel`.
|
||
|
||
`ReceiveChannel` objects also implement the `AsyncResource` interface, so
|
||
they can be closed by calling `~AsyncResource.aclose` or using an ``async
|
||
with`` block.
|
||
|
||
If you want to receive raw bytes rather than Python objects, see
|
||
`ReceiveStream`.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|
||
|
||
@abstractmethod
|
||
async def receive(self) -> ReceiveType:
|
||
"""Attempt to receive an incoming object, blocking if necessary.
|
||
|
||
Returns:
|
||
object: Whatever object was received.
|
||
|
||
Raises:
|
||
trio.EndOfChannel: if the sender has been closed cleanly, and no
|
||
more objects are coming. This is not an error condition.
|
||
trio.ClosedResourceError: if you previously closed this
|
||
:class:`ReceiveChannel` object.
|
||
trio.BrokenResourceError: if something has gone wrong, and the
|
||
channel is broken.
|
||
trio.BusyResourceError: some channels allow multiple tasks to call
|
||
`receive` at the same time, but others don't. If you try to call
|
||
`receive` simultaneously from multiple tasks on a channel that
|
||
doesn't support it, then you can get `~trio.BusyResourceError`.
|
||
|
||
"""
|
||
|
||
def __aiter__(self) -> Self:
|
||
return self
|
||
|
||
async def __anext__(self) -> ReceiveType:
|
||
try:
|
||
return await self.receive()
|
||
except trio.EndOfChannel:
|
||
raise StopAsyncIteration from None
|
||
|
||
|
||
class Channel(SendChannel[T], ReceiveChannel[T]):
|
||
"""A standard interface for interacting with bidirectional channels.
|
||
|
||
A `Channel` is an object that implements both the `SendChannel` and
|
||
`ReceiveChannel` interfaces, so you can both send and receive objects.
|
||
|
||
"""
|
||
|
||
__slots__ = ()
|