135 lines
4.6 KiB
ReStructuredText
135 lines
4.6 KiB
ReStructuredText
|
|
Contexts and streaming
|
||
|
|
======================
|
||
|
|
|
||
|
|
The modern core of ``tractor``: a :class:`Context` links a task in
|
||
|
|
one actor to a task in another as a *single* structured concurrency
|
||
|
|
(SC) scope stretched across the IPC boundary — errors, results and
|
||
|
|
cancellation flow between the pair `exactly like trio`_ tasks under
|
||
|
|
a common `nursery`_. Open one with ``Portal.open_context()`` (see
|
||
|
|
:func:`tractor._context.open_context_from_portal` in
|
||
|
|
:doc:`/api/core`), then optionally bridge a bidirectional
|
||
|
|
:class:`MsgStream` between the two tasks.
|
||
|
|
|
||
|
|
.. d2:: diagrams/context_handshake.d2
|
||
|
|
:caption: The ``open_context()`` <-> ``ctx.started()`` handshake.
|
||
|
|
:margin:
|
||
|
|
:alt: parent and child actor context handshake sequence
|
||
|
|
|
||
|
|
For the guided, example-driven tour see :doc:`/guide/context`; this
|
||
|
|
page is the precise API surface.
|
||
|
|
|
||
|
|
.. currentmodule:: tractor
|
||
|
|
|
||
|
|
The ``@context`` decorator
|
||
|
|
--------------------------
|
||
|
|
|
||
|
|
.. autofunction:: context
|
||
|
|
|
||
|
|
.. note::
|
||
|
|
|
||
|
|
The decorated function **must** declare a parameter annotated
|
||
|
|
``tractor.Context`` (any param name works); the runtime injects
|
||
|
|
the context instance there on each remote invocation. Pass
|
||
|
|
``pld_spec`` to type-restrict (and validate) the payloads this
|
||
|
|
endpoint may shuttle — violations raise
|
||
|
|
:class:`MsgTypeError`. See ``examples/typed_payloads.py``.
|
||
|
|
|
||
|
|
``Context``
|
||
|
|
-----------
|
||
|
|
|
||
|
|
.. autoclass:: Context
|
||
|
|
:members: started,
|
||
|
|
wait_for_result,
|
||
|
|
cancel,
|
||
|
|
cid,
|
||
|
|
chan,
|
||
|
|
side,
|
||
|
|
cancel_called,
|
||
|
|
cancelled_caught,
|
||
|
|
cancel_acked,
|
||
|
|
canceller,
|
||
|
|
maybe_error,
|
||
|
|
outcome
|
||
|
|
|
||
|
|
.. deprecated:: 0.1.0a6
|
||
|
|
|
||
|
|
``Context.result()`` warns; use :meth:`Context.wait_for_result`.
|
||
|
|
|
||
|
|
.. note::
|
||
|
|
|
||
|
|
A :class:`Context` is **not** a :class:`trio.CancelScope`:
|
||
|
|
:meth:`Context.cancel` requests cancellation of the *remote*
|
||
|
|
peer task and does not cancel the local scope. If *you*
|
||
|
|
requested the cancel, the resulting :class:`ContextCancelled`
|
||
|
|
is absorbed at ``open_context()`` exit; a cancel originating
|
||
|
|
anywhere else (the peer, or a third-party actor recorded in
|
||
|
|
:attr:`ContextCancelled.canceller`) *is* raised locally. This
|
||
|
|
self-vs-cross-cancel rule is the key to writing correct
|
||
|
|
inter-actor teardown logic — see :doc:`/guide/context`.
|
||
|
|
|
||
|
|
Bidirectional streaming
|
||
|
|
-----------------------
|
||
|
|
|
||
|
|
.. autofunction:: tractor._streaming.open_stream_from_ctx
|
||
|
|
|
||
|
|
.. note::
|
||
|
|
|
||
|
|
:func:`~tractor._streaming.open_stream_from_ctx` is bound as
|
||
|
|
the **method-alias** ``Context.open_stream()`` — call it as
|
||
|
|
``async with ctx.open_stream() as stream:``. Both sides of the
|
||
|
|
context must enter it for the dialog to be open.
|
||
|
|
|
||
|
|
.. autoclass:: MsgStream
|
||
|
|
:members: send,
|
||
|
|
receive,
|
||
|
|
receive_nowait,
|
||
|
|
aclose,
|
||
|
|
subscribe,
|
||
|
|
ctx,
|
||
|
|
closed
|
||
|
|
|
||
|
|
.. note::
|
||
|
|
|
||
|
|
A :class:`MsgStream` is one-shot use: once closed it can never
|
||
|
|
be "re-opened" — open a fresh :class:`Context` instead. Remote
|
||
|
|
end-of-stream surfaces as :class:`StopAsyncIteration` from
|
||
|
|
``async for``; un-consumed sends overrun the receiver and raise
|
||
|
|
:class:`tractor._exceptions.StreamOverrun` unless the context
|
||
|
|
was opened with ``allow_overruns=True``.
|
||
|
|
|
||
|
|
:meth:`MsgStream.subscribe` fans a single IPC stream out to
|
||
|
|
multiple *local* tasks via a
|
||
|
|
:class:`tractor.trionics.BroadcastReceiver` (see
|
||
|
|
:doc:`/api/trionics`); the underlying allocation is idempotent and
|
||
|
|
non-reversible for the stream's lifetime. See
|
||
|
|
``examples/streaming_broadcast_fanout.py`` for the pattern in
|
||
|
|
action.
|
||
|
|
|
||
|
|
Legacy one-way streaming
|
||
|
|
------------------------
|
||
|
|
|
||
|
|
.. autofunction:: stream
|
||
|
|
|
||
|
|
.. warning::
|
||
|
|
|
||
|
|
``@tractor.stream`` and ``Portal.open_stream_from()`` are the
|
||
|
|
*legacy* one-way streaming API kept for backward compat: a
|
||
|
|
plain async-generator function streamed parent-ward with no
|
||
|
|
child-side receive leg. New code should use
|
||
|
|
``@tractor.context`` + ``ctx.open_stream()`` (bidirectional,
|
||
|
|
SC-linked, typed). Note ``ctx`` is now a reserved param name
|
||
|
|
for ``@context`` endpoints — ``@stream`` functions must use
|
||
|
|
``stream`` instead, and ``ctx.send_yield()`` is deprecated in
|
||
|
|
favor of :meth:`MsgStream.send`.
|
||
|
|
|
||
|
|
.. seealso::
|
||
|
|
|
||
|
|
:doc:`/api/errors` for :class:`ContextCancelled` /
|
||
|
|
:class:`MsgTypeError` semantics, :doc:`/api/msg` for payload
|
||
|
|
typing via ``pld_spec`` and codecs, :doc:`/api/trionics` for
|
||
|
|
the broadcast fan-out machinery, and the guided tours in
|
||
|
|
:doc:`/guide/streaming` + :doc:`/guide/cancellation`.
|
||
|
|
|
||
|
|
.. _exactly like trio: https://trio.readthedocs.io/en/latest/reference-core.html#cancellation-semantics
|
||
|
|
.. _nursery: https://trio.readthedocs.io/en/latest/reference-core.html#nurseries-and-spawning
|