Single-consumer drainable queue for streaming events, with optional protocol auto-forwarding.
When constructed with a name, the StreamMux auto-wires every
push() to also inject a ProtocolEvent into the main event stream
using the channel's name as the method. When constructed without a
name, the channel is local-only ā items are only visible to
in-process consumers that iterate the channel directly.
Items are popped off the front as the consumer advances ā there is
no retention beyond what's currently queued. A channel accepts
exactly one subscriber; a second __iter__ / __aiter__ call
raises. Use tee(n) / atee(n) for fan-out.
Starts unbound ā neither __iter__ nor __aiter__ is available
until the StreamMux calls _bind(is_async). After binding, only
the matching iteration protocol works; the other raises TypeError.
Pump wiring (set by the run stream, not by _bind):
- _request_more: sync pump callable, returns True if a new
event was produced.
- _arequest_more: async pump coroutine factory, same contract.
Memory is bounded by caller pace: both sync and async use caller- driven pumps, so each cursor advance produces at most one event.
Lazy-subscribe: push appends to the local buffer only when a
subscriber has registered. Auto-forward via _wire_fn always fires
regardless of subscription state.
Lifecycle (close / fail) is managed by the mux ā transformers
don't need to close their channels manually.
Optional protocol channel name. When set, the
StreamMux wires every push() to also inject a
ProtocolEvent into the main event stream. Surfaced
on the wire as custom:<name> for user-defined
transformers, or as <name> for channels owned by a
native transformer (_native = True). When None,
the channel is local-only.
Accepted for forward compatibility; currently unused. The caller-driven pump bounds memory naturally for single-consumer use.
Append an item. Auto-forwards if wired.
The local buffer append is a no-op when no subscriber is registered, but auto-forwarding always fires so wired events reach the main event log regardless of subscription state.
Mark the channel as complete.
Mark the channel as errored.
Subscribe and return n independent sync iterators.
Each branch has its own buffer; items pulled from the underlying cursor are copied into every branch. Branches are naturally bounded by caller pace since the sync pump is caller-driven.
Subscribe and return n independent async iterators.
Caller-driven fan-out: each branch's __anext__ either pops
from its own buffer or, under a shared asyncio.Lock, pulls
one item from the underlying cursor and distributes it to
every branch's buffer.