A store of messages for a set of subscribers. Not thread-safe: use it
from one scheduler thread, as a PubSub does.
Attributes#
| Attribute | Type | Description |
|---|---|---|
capacity | int | None |
Functions#
__init__#
__init__(capacity, replay=None) -> None| Parameter | Type | Description |
|---|---|---|
capacity | int | None | |
replay | int | None = None |
def __init__(self, capacity: int | None, replay: int | None = None) -> None:
if capacity is not None and capacity <= 0:
raise ValueError(f"Cannot construct PubSub with capacity of {capacity}")
self._capacity = capacity
self._replay = ReplayBuffer[A](replay) if replay is not None and replay > 0 else None
self._slots: deque[_Slot[A]] = deque()
# The publish index of the oldest stored message.
self._start = 0
self._subscribers = 0bounded#
bounded(capacity, *, replay=None) -> Atomic[A]A store of at most capacity messages (None: unbounded),
replaying the last replay to new subscribers (Effect's
makeAtomicBounded). A capacity below 1 is a ValueError.
| Parameter | Type | Description |
|---|---|---|
capacity | int | None | |
replay | int | None = None |
Returns Atomic[A]
@classmethod
def bounded(cls, capacity: int | None, *, replay: int | None = None) -> Atomic[A]:
"""A store of at most ``capacity`` messages (``None``: unbounded),
replaying the last ``replay`` to new subscribers (Effect's
``makeAtomicBounded``). A capacity below 1 is a ``ValueError``."""
return cls(capacity, replay)unbounded#
unbounded(*, replay=None) -> Atomic[A]Effect's makeAtomicUnbounded.
| Parameter | Type | Description |
|---|---|---|
replay | int | None = None |
Returns Atomic[A]
@classmethod
def unbounded(cls, *, replay: int | None = None) -> Atomic[A]:
"""Effect's ``makeAtomicUnbounded``."""
return cls(None, replay)size#
size() -> intHow many messages are stored (each once, however many readers).
Returns int
def size(self) -> int:
"""How many messages are stored (each once, however many readers)."""
return len(self._slots)is_empty#
is_empty() -> boolReturns bool
def is_empty(self) -> bool:
return not self._slotsis_full#
is_full() -> boolReturns bool
def is_full(self) -> bool:
return len(self._slots) == self._capacitypublish#
publish(value) -> boolStore value for the current subscribers; False if full.
| Parameter | Type | Description |
|---|---|---|
value | A |
Returns bool
def publish(self, value: A) -> bool:
"""Store ``value`` for the current subscribers; ``False`` if full."""
if self.is_full():
return False
replay_index = 0 if self._replay is None else self._replay.offer(value)
if self._subscribers:
self._slots.append(_Slot(value, self._subscribers, replay_index))
return Truepublish_all#
publish_all(items) -> list[A]Store what fits, in order; the items that did not.
| Parameter | Type | Description |
|---|---|---|
items | Iterable[A] |
Returns list[A]
def publish_all(self, items: Iterable[A]) -> list[A]:
"""Store what fits, in order; the items that did not."""
if not self._subscribers:
if self._replay is not None:
for item in items:
self._replay.offer(item)
return []
batch = list(items)
fits = len(batch) if self._capacity is None else self._capacity - len(self._slots)
for item in batch[:fits]:
self.publish(item)
return batch[fits:]slide#
slide() -> NoneDrop the oldest stored message for every subscriber.
def slide(self) -> None:
"""Drop the oldest stored message for every subscriber."""
if self._slots:
slot = self._slots.popleft()
self._start += 1
if self._replay is not None:
self._replay.slide(slot.value, slot.replay_index)subscribe#
subscribe() -> BackingSubscription[A]A reader of the messages published from now on.
Returns BackingSubscription[A]
def subscribe(self) -> BackingSubscription[A]:
"""A reader of the messages published from now on."""
self._subscribers += 1
return BackingSubscription(self)replay_window#
replay_window() -> ReplayWindow[A]The messages a subscriber that joins now replays.
Returns ReplayWindow[A]
def replay_window(self) -> ReplayWindow[A]:
"""The messages a subscriber that joins now replays."""
return ReplayWindow(self._replay)