Source code for eventkit.event

import types
import weakref
import asyncio
import logging
from typing import List, Union, Iterable, Awaitable, AsyncIterable

from .util import NO_VALUE, loop


class Event:
    """
    Enable event passing between loosly coupled components.
    The event emits values to connected listeners and has
    a selection of operators to create general data flow pipelines.

    Args:
        name: Name to use for this event.
    """
    __slots__ = (
        'error_event', 'done_event', '_name', '_value',
        '_slots', '_done', '_source', '__weakref__')

    NO_VALUE = NO_VALUE
    logger = logging.getLogger(__name__)

    def __init__(self, name: str = '', _with_error_done_events: bool = True):
        self.error_event = None
        """
        Sub event that emits errors from this event as
        ``emit(source, exception)``.
        """
        self.done_event = None
        """
        Sub event that emits when this event is done as
        ``emit(source)``.
        """
        if _with_error_done_events:
            self.error_event = Event('error', False)
            self.done_event = Event('done', False)
        self._slots = []  # list of [obj, weakref, func] sublists
        self._name = name or self.__class__.__qualname__
        self._value = NO_VALUE
        self._done = False
        self._source = None

    def name(self) -> str:
        """
        This event's name.
        """
        return self._name

    def done(self) -> bool:
        """
        ``True`` if event has ended with no more emits coming,
        ``False`` otherwise.
        """
        return self._done

    def set_done(self):
        """
        Set this event to be ended. The event should not emit anything
        after that.
        """
        if not self._done:
            self._done = True
            self.done_event.emit(self)

    def value(self):
        """
        This event's last emitted value.
        """
        v = self._value
        return NO_VALUE if v is NO_VALUE else \
            v[0] if len(v) == 1 else v if v else NO_VALUE

    def connect(self, listener, error=None, done=None, keep_ref: bool = False):
        """
        Connect a listener to this event. If the listener is added multiple
        times then it is invoked just as many times on emit.

        The ``+=`` operator can be used as a synonym for this method::

            import eventkit as ev

            def f(a, b):
                print(a * b)

            def g(a, b):
                print(a / b)

            event = ev.Event()
            event += f
            event += g
            event.emit(10, 5)

        Args:
            listener: The callback to invoke on emit of this event.
                It gets the ``*args`` from an emit as arguments.
            error: The callback to invoke on error of this event.
                It gets (this event, exception) as two arguments.
            done: The callback to invoke on ending of this event.
                It gets this event as single argument.
            keep_ref:
                * ``True``: A strong reference to the callable is kept
                * ``False``: If the callable allows weak refs and it is
                  garbage collected, then it is automatically disconnected
                  from this event.
        """
        obj, func = self._split(listener)
        if not keep_ref and hasattr(obj, '__weakref__'):
            ref = weakref.ref(obj, self._onFinalize)
            obj = None
        else:
            ref = None
        slot = [obj, ref, func]
        self._slots.append(slot)
        if done is not None:
            self.done_event.connect(done)
        if error is not None:
            self.error_event.connect(error)
        return self

    def disconnect(self, listener, error=None, done=None):
        """
        Disconnect a listener from this event.

        The ``-=`` operator can be used as a synonym for this method.

        Args:
            listener: The callback to disconnect. The callback is removed at
                most once. It is valid if the callback is already
                not connected.
            error: The error callback to disconnect.
            done: The done callback to disconnect.
        """
        obj, func = self._split(listener)
        for slot in self._slots:
            if (slot[0] is obj or slot[1] and slot[1]() is obj) \
                    and slot[2] is func:
                slot[0] = slot[1] = slot[2] = None
                break
        self._slots = [s for s in self._slots if s != [None, None, None]]
        if error is not None:
            self.error_event.disconnect(error)
        if done is not None:
            self.done_event.disconnect(done)
        return self

    def disconnect_obj(self, obj):
        """
        Disconnect all listeners on the given object.
        (also the error and done listeners).

        Args:
            obj: The target object that is to be completely removed from
              this event.
        """
        for slot in self._slots:
            if slot[0] is obj or slot[1] and slot[1]() is obj:
                slot[0] = slot[1] = slot[2] = None
        self._slots = [s for s in self._slots if s != [None, None, None]]
        if self.error_event is not None:
            self.error_event.disconnect_obj(obj)
        if self.done_event is not None:
            self.done_event.disconnect_obj(obj)

    def emit(self, *args):
        """
        Emit a new value to all connected listeners.

        Args:
            args: Argument values to emit to listeners.
        """
        self._value = args
        for obj, ref, func in self._slots:
            try:
                if ref:
                    obj = ref()
                if obj is None:
                    if func:
                        func(*args)
                else:
                    if func:
                        func(obj, *args)
                    else:
                        obj(*args)
            except Exception as error:
                if len(self.error_event):
                    self.error_event.emit(self, error)
                else:
                    Event.logger.exception(
                        f'Value {args} caused exception for event {self}')

    def emit_threadsafe(self, *args):
        """
        Threadsafe version of :meth:`emit` that doesn't invoke the
        listeners directly but via the event loop.
        """
        loop.call_soon_threadsafe(self.emit, *args)

    def clear(self):
        """
        Disconnect all listeners.
        """
        for slot in self._slots:
            slot[0] = slot[1] = slot[2] = None
        self._slots = []

    def run(self) -> List:
        """
        Start the asyncio event loop, run this event to completion and
        return all values as a list::

            import eventkit as ev

            ev.Timer(0.25, count=10).run()
            ->
            [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5]

        .. note::

            When running inside a Jupyter notebook this will give an error
            that the asyncio event loop is already running. This can be
            remedied by applying
            `nest_asyncio <https://github.com/erdewit/nest_asyncio>`_
            or by using the top-level ``await`` statement of Jupyter::

                await event.list()
        """
        return loop.run_until_complete(self.list())

    def pipe(self, *targets: "Event"):
        """
        Form several events into a pipe::

            import eventkit as ev

            e1 = ev.Sequence('abcde')
            e2 = ev.Enumerate().map(lambda i, c: (i, i + ord(c)))
            e3 = ev.Star().pluck(1).map(chr)

            e1.pipe(e2, e3)     # or: ev.Event.Pipe(e1, e2, e3)
            ->
            ['a', 'c', 'e', 'g', 'i']

        Args:
            targets: One or more Events that have no source yet,
                or ``Event`` constructors that needs no arguments.
        """
        source = self
        for t in targets:
            t = Event.create(t)
            t.set_source(source)
            source = t
        return source

    def fork(self, *targets: "Event") -> "Fork":
        """
        Fork this event into one or more target events.
        Square brackets can be used as a synonym::

            import eventkit as ev

            ev.Range(2, 5)[ev.Min, ev.Max, ev.Sum].zip()
            ->
            [(2, 2, 2), (2, 3, 5), (2, 4, 9)]

        The events in the fork can be combined by one of the join
        methods of ``Fork``.

        Args:
            targets: One or more events that have no source yet,
                or ``Event`` constructors that need no arguments.
        """
        fork = Fork()
        for t in targets:
            t = Event.create(t)
            t.set_source(self)
            fork.append(t)
        return fork

    def set_source(self, source):
        self._source = source

    def _onFinalize(self, ref):
        for slot in self._slots:
            if slot[1] is ref:
                slot[0] = slot[1] = slot[2] = None
        self._slots = [s for s in self._slots if s != [None, None, None]]

    @staticmethod
    def _split(c):
        """
        Split given callable in (object, function) tuple.
        """
        if isinstance(c, types.FunctionType):
            return (None, c)
        elif isinstance(c, types.MethodType):
            return (c.__self__, c.__func__)
        elif isinstance(c, types.BuiltinMethodType):
            if type(c.__self__) is type:
                # built-in method
                return (c.__self__, c)
            else:
                # built-in function
                return (None, c)
        elif hasattr(c, '__call__'):
            return (c, None)
        else:
            raise ValueError(f'Invalid callable: {c}')

    async def aiter(self, skip_to_last: bool = False, tuples: bool = False):
        """
        Create an asynchronous iterator that yields the emitted values
        from this event::

            async def coro():
                async for args in event.aiter():
                    ...

        :meth:`__aiter__` is a synonym for :meth:`aiter` with
        default arguments,

        Args:
            skip_to_last:
                * ``True``: Backlogged source values are skipped over to
                  yield only the latest value. Can be used as a
                  slipper clutch between a source that produces too fast
                  and the handling that can't keep up.
                * ``False``: All events are yielded.
            tuples:
                * ``True``: Always yield arguments as a tuple.
                * ``False``: Unpack single argument tuples.
        """
        def on_event(*args):
            q.put_nowait((None, args))

        def on_error(source, error):
            q.put_nowait(('ERROR', error))

        def on_done(source):
            q.put_nowait(('DONE', None))

        if self.done():
            return
        q = asyncio.Queue()
        self.connect(on_event, on_error, on_done)
        try:
            while True:
                what, args = await q.get()
                if skip_to_last:
                    while q.qsize():
                        what, args = q.get_nowait()
                if what is None:
                    yield args if tuples else args[0] if len(args) == 1 \
                        else args if args else NO_VALUE
                elif what == 'ERROR':
                    raise args
                else:
                    break
        finally:
            self.disconnect(on_event, on_error, on_done)

    __iadd__ = connect
    __isub__ = disconnect
    __call__ = emit
    __or__ = pipe

    def __repr__(self):
        return f'Event<{self.name()}, {self._slots}>'

    def __len__(self):
        return len(self._slots)

    def __bool__(self):
        return True

    def __getitem__(self, fork_targets) -> "Fork":
        if not hasattr(fork_targets, '__iter__'):
            fork_targets = (fork_targets,)
        return self.fork(*fork_targets)

    def __await__(self):
        """
        Asynchronously await the next emit of an event::

            async def coro():
                args = await event
                ...

        If the event does an empty ``emit()``, then the value
        of ``args`` is set to ``util.NO_VALUE``.

        :meth:`wait` and :meth:`__await__` are each other's inverse.
        """
        def on_event(*args):
            if not fut.done():
                fut.set_result(
                    args[0] if len(args) == 1 else args if args else NO_VALUE)

        def on_error(source, error):
            if not fut.done():
                fut.set_exception(error)

        def on_future_done(f):
            self.disconnect(on_event, on_error)

        if self.done():
            raise ValueError('Event already done')
        fut = asyncio.Future()
        self.connect(on_event, on_error)
        fut.add_done_callback(on_future_done)
        return fut.__await__()

    __aiter__ = aiter
    """
    Synonym for :meth:`aiter` with default arguments::

        async def coro():
            async for args in event:
                ...

    :meth:`aiterate` and :meth:`__aiter__` are each other's inverse.
    """

    def __contains__(self, c):
        """
        See if callable is already connected.
        """
        obj, func = self._split(c)
        return any(
            (s[0] is obj or s[1] and s[1]() is obj) and s[2] is func
            for s in self._slots)

    def __reduce__(self):
        """
        Don't pickle slots.
        """
        with_error_done_event = (
            self.error_event is not None or self.done_event is not None)
        return self.__class__, (self._name, with_error_done_event)

    @staticmethod
    def init(obj, event_names: Iterable):
        """
        Convenience function for initializing multiple events as members
        of the given object.

        Args:
            event_names: Names to use for the created events.
        """
        for name in event_names:
            setattr(obj, name, Event(name))

    # dot access to constructors

    @staticmethod
    def create(obj):
        """
        Create an event from a async iterator, awaitable, or event
        constructor without arguments.

        Args:
            obj: The source object. If it's already an event then it
              is passed as-is.
        """
        if isinstance(obj, Event):
            return obj
        if hasattr(obj, '__call__'):
            obj = obj()

        if isinstance(obj, Event):
            return obj
        elif hasattr(obj, '__aiter__'):
            return Event.aiterate(obj)
        elif hasattr(obj, '__await__'):
            return Event.wait(obj)
        else:
            raise ValueError(f'Invalid type: {obj}')

    @staticmethod
    def wait(future: Awaitable) -> "Wait":
        """
        Create a new event that emits the value of the
        awaitable when it becomes available and then set this event done.

        :meth:`wait` and :meth:`__await__` are each other's inverse.

        Args:
            future: Future to wait on.
        """
        return Wait(future)

    @staticmethod
    def aiterate(ait: AsyncIterable) -> "Aiterate":
        """
        Create a new event that emits the yielded values from the
        asynchronous iterator.

        The asynchronous iterator serves as a source for both the time
        and value of emits.

        :meth:`aiterate` and :meth:`__aiter__` are each other's inverse.

        Args:
            ait: The asynchronous source iterator. It must ``await``
                at least once; If necessary use::

                    await asyncio.sleep(0)
        """
        return Aiterate(ait)

    @staticmethod
    def sequence(
            values: Iterable, interval: float = 0,
            times: Iterable[float] = None) -> "Sequence":
        """
        Create a new event that emits the given values.
        Supply at most one ``interval`` or ``times``.

        Args:
            value: The source values.
            interval: Time interval in seconds between values.
            times: Relative times for individual values, in seconds since
                start of event. The sequence should match ``values``.
        """
        return Sequence(values, interval, times)

    @staticmethod
    def repeat(
            value=NO_VALUE, count=1, interval: float = 0,
            times: Iterable[float] = None) -> "Repeat":
        """
        Create a new event that repeats ``value`` a number of ``count`` times.

        Args:
            value: The value to emit.
            count: Number of times to emit.
            interval: Time interval in seconds between values.
            times: Relative times for individual values, in seconds since
                start of event. The sequence should match ``values``.
        """
        return Repeat(interval, value, count)

    @staticmethod
    def range(
            *args, interval: float = 0,
            times: Iterable[float] = None) -> "Range":
        """
        Create a new event that emits the values from a range.

        Args:
            args: Same as for built-in ``range``.
            interval: Time interval in seconds between values.
            times: Relative times for individual values, in seconds since
                start of event. The sequence should match the range.
        """
        return Range(*args, interval=interval, times=times)

    @staticmethod
    def timerange(start=0, end=None, step=1) -> "Timerange":
        """
        Create a new event that emits the datetime value, at that datetime,
        from a range of datetimes.

        Args:
            start: Start time, can be specified as:

                * ``datetime.datetime``.
                * ``datetime.time``: Today is used as date.
                * ``int`` or ``float``: Number of seconds relative to now.
                  Values will be quantized to the given step.
            end: End time, can be specified as:

                * ``datetime.datetime``.
                * ``datetime.time``: Today is used as date.
                * ``None``: No end limit.
            step: Number of seconds, or ``datetime.timedelta``,
                to space between values.
        """
        return Timerange(start, end, step)

    @staticmethod
    def timer(interval: float, count: int = None) -> "Timer":
        """
        Create a new timer event that emits at regularly paced intervals
        the number of seconds since starting it.

        Args:
            interval: Time interval in seconds between emits.
            count: Number of times to emit, or ``None`` for no limit.
        """
        return Timer(interval, count)

    @staticmethod
    def marble(
            s: str, interval: float = 0,
            times: Iterable[float] = None) -> "Marble":
        """
        Create a new event that emits the values from a Rx-type marble string.

        Args:
            s: The string with characters that are emitted.
            interval: Time interval in seconds between values.
            times: Relative times for individual values, in seconds since
                start of event. The sequence should match the marble string.

        """
        return Marble(s, interval, times)

    # dot access to operators

    def filter(self, predicate=bool) -> "Filter":
        """
        For every source value, apply predicate and re-emit when True.

        Args:
            predicate: The function to test every source value with.
                The default is to test the general truthiness with ``bool()``.
        """
        return Filter(predicate, self)

    def skip(self, count: int = 1) -> "Skip":
        """
        Drop the first ``count`` values from source and follow the source
        after that.

        Args:
            count: Number of source values to drop.
        """
        return Skip(count, self)

    def take(self, count: int = 1) -> "Take":
        """
        Re-emit first ``count`` values from the source and then end.

        Args:
            count: Number of source values to re-emit.
        """
        return Take(count, self)

    def takewhile(self, predicate=bool) -> "TakeWhile":
        """
        Re-emit values from the source until the predicate becomes False
        and then end.

        Args:
            predicate: The function to test every source value with.
                The default is to test the general truthiness with ``bool()``.
        """
        return TakeWhile(predicate, self)

    def dropwhile(self, predicate=lambda x: not(x)) -> "DropWhile":
        """
        Drop source values until the predicate becomes False and after that
        re-emit everything from the source.

        Args:
            predicate: The function to test every source value with.
                The default is to test the inverted general truthiness.
        """
        return DropWhile(predicate, self)

    def takeuntil(self, notifier: "Event") -> "TakeUntil":
        """
        Re-emit values from the source until the ``notifier`` emits
        and then end. If the notifier ends without any emit then
        keep passing source values.

        Args:
            notifier: Event that signals to end this event.
        """
        return TakeUntil(notifier, self)

    def constant(self, constant) -> "Constant":
        """
        On emit of the source emit a constant value::

            emit(value) -> emit(constant)

        Args:
            constant: The constant value to emit.
        """
        return Constant(constant, self)

    def iterate(self, it) -> "Iterate":
        """
        On emit of the source, emit the next value from an iterator::

            emit(a, b, ...) -> emit(next(it))

        The time of events follows the source and the values follow
        the iterator.

        Args:
            it: The source iterator to use for generating values. When the
                iterator is exhausted the event is set to be done.
        """
        return Iterate(it, self)

    def count(self, start=0, step=1) -> "Count":
        """
        Count and emit the number of source emits::

            emit(a, b, ...) -> emit(count)

        Args:
            start: Start count.
            step: Add count by this amount for every new source value.
        """
        return Count(start, step, self)

    def enumerate(self, start=0, step=1) -> "Enumerate":
        """
        Add a count to every source value::

            emit(a, b, ...) -> emit(count, a, b, ...)

        Args:
            start: Start count.
            step: Increase by this amount for every new source value.
        """
        return Enumerate(start, step, self)

    def timestamp(self) -> "Timestamp":
        """
        Add a timestamp (from time.time()) to every source value::

            emit(a, b, ...) -> emit(timestamp, a, b, ...)

        The timestamp is the float number in seconds since the
        midnight Jan 1, 1970 epoch.
        """
        return Timestamp(self)

    def partial(self, *left_args) -> "Partial":
        """
        Pad source values with extra arguments on the left::

            emit(a, b, ...) -> emit(*left_args, a, b, ...)

        Args:
            left_args: Arguments to inject.
        """
        return Partial(*left_args, source=self)

    def partial_right(self, *right_args) -> "PartialRight":
        """
        Pad source values with extra arguments on the right::

            emit(a, b, ...) -> emit(a, b, ..., *right_args)

        Args:
            right_args: Arguments to inject.
        """
        return PartialRight(*right_args, source=self)

    def star(self) -> "Star":
        """
        Unpack a source tuple into positional arguments, similar to the
        star operator::

            emit((a, b, ...)) -> emit(a, b, ...)

        :meth:`star` and :meth:`pack` are each other's inverse.
        """
        return Star(self)

    def pack(self) -> "Pack":
        """
        Pack positional arguments into a tuple::

            emit(a, b, ...) -> emit((a, b, ...))

        :meth:`star` and :meth:`pack` are each other's inverse.
        """
        return Pack(self)

    def pluck(self, *selections: Union[int, str]) -> "Pluck":
        """
        Extract arguments or nested properties from the source values.

        Select which argument positions to keep::

            emit(a, b, c, d).pluck(1, 2) -> emit(b, c)

        Re-order arguments::

            emit(a, b, c).pluck(2, 1, 0) -> emit(c, b, a)

        To do an empty emit leave ``selections`` empty::

            emit(a, b).pluck() -> emit()

        Select nested properties from positional arguments::

            emit(person, account).pluck(
                '1.number', '0.address.street') ->

            emit(account.number, person.address.street)

        If no value can be extracted then ``NO_VALUE`` is emitted in its place.

        Args:
            selections: The values to extract.
        """
        return Pluck(*selections, source=self)

    def map(
            self, func, timeout=None, ordered=True,
            task_limit=None) -> "Map":
        """
        Apply a sync or async function to source values using
        positional arguments::

            emit(a, b, ...) -> emit(func(a, b, ...))

        or if ``func`` returns an awaitable then it will be awaited::

            emit(a, b, ...) -> emit(await func(a, b, ...))

        In case of timeout or other failure, ``NO_VALUE`` is emitted.

        Args:
            func: The function or coroutine constructor to apply.
            timeout: Timeout in seconds since coroutine is started
            ordered:
                * ``True``: The order of emitted results preserves the
                  order of the source values.
                * ``False``: Results are in order of completion.
            task_limit: Max number of concurrent tasks, or None for no limit.

        ``timeout``, ``ordered`` and ``task_limit`` apply to
        async functions only.
        """
        return Map(func, timeout, ordered, task_limit, self)

    def emap(self, constr, joiner: "AddableJoinOp") -> "Emap":
        """
        Higher-order event map that creates a new ``Event`` instance
        for every source value::

            emit(a, b, ...) -> new Event constr(a, b, ...)

        Args:
            constr: Constructor function for creating a new event.
                Apart from returning  an ``Event``, the constructor may also
                return an awaitable or an asynchronous iterator, in which
                case an ``Event`` will be created.
            joiner: Join operator to combine the emits of nested events.
        """
        return Emap(constr, joiner, self)

    def mergemap(self, constr) -> "Mergemap":
        """
        :meth:`emap` that uses :meth:`merge` to combine the nested events::

            marbles = [
                'A   B    C    D',
                '_1   2  3    4',
                '__K   L     M   N']

            ev.Range(3).mergemap(lambda v: ev.Marble(marbles[v]))
            ->
            ['A', '1', 'K', 'B', '2', 'L', '3', 'C', 'M', '4', 'D', 'N']
        """
        return Mergemap(constr, self)

    def concatmap(self, constr) -> "Concatmap":
        """
        :meth:`emap` that uses :meth:`concat` to combine the nested events::

            marbles = [
                'A    B    C    D',
                '_       1    2    3    4',
                '__                  K    L      M   N']

            ev.Range(3).concatmap(lambda v: ev.Marble(marbles[v]))
            ->
            ['A', 'B', '1', '2', '3', 'K', 'L', 'M', 'N']
        """
        return Concatmap(constr, self)

    def chainmap(self, constr) -> "Chainmap":
        """
        :meth:`emap` that uses :meth:`chain` to combine the nested events::

            marbles = [
                'A    B    C    D           ',
                '_       1    2    3    4',
                '__                  K    L      M   N']

            ev.Range(3).chainmap(lambda v: ev.Marble(marbles[v]))
            ->
            ['A', 'B', 'C', 'D', '1', '2', '3', '4', 'K', 'L', 'M', 'N']
        """
        return Chainmap(constr, self)

    def switchmap(self, constr) -> "Switchmap":
        """
        :meth:`emap` that uses :meth:`switch` to combine the nested events::

            marbles = [
                'A    B    C    D           ',
                '_                 K    L      M   N',
                '__      1    2      3    4'
            ]
            ev.Range(3).switchmap(lambda v: Event.marble(marbles[v]))
            ->
            ['A', 'B', '1', '2', 'K', 'L', 'M', 'N'])
        """
        return Switchmap(constr, self)

    def reduce(self, func, initializer=NO_VALUE) -> "Reduce":
        """
        Apply a two-argument reduction function to the previous reduction
        result and the current value and emit the new reduction result.

        Args:
            func: Reduction function::

                emit(args) -> emit(func(prev_args, args))

            initializer: First argument of first reduction::

                    first_result = func(initializer, first_value)

                If no initializer is given, then the first result is
                emitted on the second source emit.
        """
        return Reduce(func, initializer, self)

    def min(self) -> "Min":
        """
        Minimum value.
        """
        return Min(self)

    def max(self) -> "Max":
        """
        Maximum value.
        """
        return Max(self)

    def sum(self, start=0) -> "Sum":
        """
        Total sum.

        Args:
            start: Value added to total sum.
        """
        return Sum(start, self)

    def product(self, start=1) -> "Product":
        """
        Total product.

        Args:
            start: Initial start value.
        """
        return Product(start, self)

    def mean(self) -> "Mean":
        """
        Total average.
        """
        return Mean(self)

    def any(self) -> "Any":
        """
        Test if predicate holds for at least one source value.
        """
        return Any(self)

    def all(self) -> "All":
        """
        Test if predicate holds for all source values.
        """
        return All(self)

    def ema(self, n: int = None, weight: float = None) -> "Ema":
        """
        Exponential moving average.

        Args:
            n: Number of periods.
            weight: Weight of new value.

        Give either ``n`` or ``weight``.
        The relation is ``weight = 2 / (n + 1)``.
        """
        return Ema(n, weight, self)

    def previous(self, count: int = 1) -> "Previous":
        """
        For every source value, emit the ``count``-th previous value::

            source:  -ab---c--d-e-
            output:  --a---b--c-d-

        Starts emitting on the ``count + 1``-th source emit.

        Args:
            count: Number of periods to go back.
        """
        return Previous(count, self)

    def pairwise(self) -> "Pairwise":
        """
        Emit ``(previous_source_value, current_source_value)`` tuples.
        Starts emitting on the second source emit::

            source:  -a----b------c--------d-----
            output:  ------(a,b)--(b,c)----(c,d)-
        """
        return Pairwise(self)

    def changes(self) -> "Changes":
        """
        Emit only source values that have changed from the previous value.
        """
        return Changes(self)

    def unique(self, key=None) -> "Unique":
        """
        Emit only unique values, dropping values that have already
        been emitted.

        Args:
            key: `The callable `'key(value)`` is used to group values.
                The default of ``None`` groups values by equality.
                The resulting group must be hashable.
        """
        return Unique(key, self)

    def last(self) -> "Last":
        """
        Wait until source has ended and re-emit its last value.
        """
        return Last(self)

    def list(self) -> "List":
        """
        Collect all source values and emit as list when the source ends.
        """
        return List(self)

    def deque(self, count=0) -> "Deque":
        """
        Emit a ``deque`` with the last ``count`` values from the source
        (or less in the lead-in phase).

        Args:
            count: Number of last periods to use, or 0 to use all.
        """
        return Deque(count, self)

    def array(self, count=0) -> "Array":
        """
        Emit a numpy array with the last ``count`` values from the source
        (or less in the lead-in phase).

        Args:
            count: Number of last periods to use, or 0 to use all.
        """
        return Array(count, self)

    def chunk(self, size: int) -> "Chunk":
        """
        Chunk values up in lists of equal size. The last chunk can be shorter.

        Args:
            size: Chunk size.
        """
        return Chunk(size, self)

    def chunkwith(
            self, timer: "Event", emit_empty: bool = True) -> "ChunkWith":
        """
        Emit a chunked list of values when the timer emits.

        Args:
            timer: Event to use for timing the chunks.
            emit_empty: Emit empty list if no values present since last emit.
        """
        return ChunkWith(timer, emit_empty, self)

    def chain(self, *sources: "Event") -> "Chain":
        """
        Re-emit from a source until it ends, then move to the next source,
        Repeat until all sources have ended, ending the chain.
        Emits from pending sources are queued up::

            source 1:  -a----b---c|
            source 2:        --2-----3--4|
            source 3:  ------------x---------y--|
            output:    -a----b---c2--3--4x---y--|


        Args:
            sources: Source events.
        """
        return Chain(self, *sources)

    def merge(self, *sources) -> "Merge":
        """
        Re-emit everything from the source events::

            source 1:  -a----b-------------c------d-|
            source 2:     ------1-----2------3--4-|
            source 3:      --------x----y--|
            output:    -a----b--1--x--2-y--c-3--4-d-|

        Args:
            sources: Source events.
        """
        return Merge(self, *sources)

    def concat(self, *sources) -> "Concat":
        """
        Re-emit everything from one source until it ends and then move
        to the next source::

            source 1:  -a----b-----|
            source 2:    --1-----2-----3----4--|
            source 3:                 -----------x--y--|
            output:    -a----b---------3----4----x--y--|

        Args:
            sources: Source events.
        """
        return Concat(self, *sources)

    def switch(self, *sources) -> "Switch":
        """
        Re-emit everything from one source and move to another source as soon
        as that other source starts to emit::

            source 1:  -a----b---c-----d---|
            source 2:        -----------x---y-|
            source 3:  ---------1----2----3-----|
            output:    -a----b--1----2--x---y---|

        Args:
            sources: Source events.
        """
        return Switch(self, *sources)

    def zip(self, *sources) -> "Zip":
        """
        Zip sources together: The i-th emit has the i-th value from
        each source as positional arguments. Only emits when each source has
        emtted its i-th value and ends when any source ends::

            source 1:    -a----b------------------c------d---e--f---|
            source 2:    --------1-------2-------3---------4-----|
            output emit: --------(a,1)---(b,2)----(c,3)----(d,4)-|


        Args:
            sources: Source events.
        """
        return Zip(self, *sources)

    def ziplatest(self, *sources, partial: bool = True) -> "Ziplatest":
        """
        Emit zipped values with the latest value from each of the
        source events. Emits every time when a source emits::

            source 1:   -a-------------------b-------c---|
            source 2:   ---------------1--------------------2------|
            output emit: (a,NoValue)---(a,1)-(b,1)---(c,1)--(c,2)--|

        Args:
            sources: Source events.
            partial:
                * True: Use ``NoValue`` for sources that have not emitted yet.
                * False: Wait until all sources have emitted.
        """
        return Ziplatest(self, *sources, partial=partial)

    def delay(self, delay) -> "Delay":
        """
        Time-shift all source events by a delay::

            source:  -abc-d-e---f---|
            output:  ---abc-d-e---f---|

        This applies to the source errors and the source done event as well.

        Args:
            delay: Time delay of all events (in seconds).
        """
        return Delay(delay, self)

    def timeout(self, timeout) -> "Timeout":
        """
        When the source doesn't emit for longer than the timeout period,
        do an empty emit and set this event as done.

        Args:
            timeout: Timeout value.
        """
        return Timeout(timeout, self)

    def throttle(
            self, maximum, interval, cost_func=None) -> "Throttle":
        """
        Limit number of emits per time without dropping values.
        Values that come in too fast are queued and re-emitted as soon
        as allowed by the limits.

        A nested ``status_event`` emits ``True`` when throttling starts
        and ``False`` when throttling ends.

        The limit can be dynamically changed with ``set_limit``.

        Args:
            maximum: Maximum payload per interval.
            interval: Time interval (in seconds).
            cost_func: The sum of ``cost_func(value)`` for every
                source value inside the ``interval`` that is to remain
                under the ``maximum``. The default is to count every
                source value as 1.
        """
        return Throttle(maximum, interval, cost_func, self)

    def debounce(self, delay, on_first: bool = False) -> "Debounce":
        """
        Filter out values from the source that happen in rapid succession.

        Args:
            delay: Maximal time difference (in seconds) between
                successive values before debouncing kicks in.
            on_first:
                * True: First value is send immediately and following values
                  in the rapid succession are dropped::

                    source: -abcd----efg-
                    output: -a-------e---

                * False: Last value of a rapid succession is send after
                  the delay and the values before that are dropped::

                    source:  -abcd----efg--
                    output:   ----d------g-
        """
        return Debounce(delay, on_first, self)

    def copy(self) -> "Copy":
        """
        Create a shallow copy of the source values.
        """
        return Copy(self)

    def deepcopy(self) -> "Deepcopy":
        """
        Create a deep copy of the source values.
        """
        return Deepcopy(self)

    def sample(self, timer: "Event") -> "Sample":
        """
        At the times that the timer emits, sample the value from this
        event and emit the sample.

        Args:
            timer: Event used to time the samples.
        """
        return Sample(timer, self)

    def errors(self) -> "Errors":
        """
        Emit errors from the source.
        """
        return Errors(self)

    def end_on_error(self) -> "EndOnError":
        """
        End on any error from the source.
        """
        return EndOnError(self)


from .ops.op import Op  # noqa
from .ops.create import (
    Wait, Aiterate, Sequence, Repeat, Range, Timerange, Timer, Marble)  # noqa
from .ops.combine import (Fork, AddableJoinOp, Chain, Merge, Concat, Switch,
    Zip, Ziplatest)  # noqa
from .ops.select import (
    Filter, Skip, Take, TakeWhile, DropWhile, TakeUntil, Changes,
    Unique, Last)  # noqa
from .ops.transform import (
    Constant, Iterate, Enumerate, Timestamp, Chunk, ChunkWith,
    Map, Emap, Mergemap, Chainmap, Concatmap, Switchmap,
    Partial, PartialRight, Star, Pack, Pluck,
    Previous, Copy, Deepcopy)  # noqa
from .ops.array import (Array, ArrayMin, ArrayMax, ArraySum,  # noqa
    ArrayProd, ArrayMean, ArrayStd, ArrayAny, ArrayAll) # noqa
from .ops.aggregate import (
    Count, Reduce, Min, Max, Sum, Product, Mean, Any, All,
    Ema, Pairwise, List, Deque)  # noqa
from .ops.timing import (
    Delay, Timeout, Throttle, Debounce, Sample)  # noqa
from .ops.misc import Errors, EndOnError  # noqa