def test_on_error(self):
        init_flowable(self.source).to_rx().subscribe(self.sink,
                                                     scheduler=self.scheduler)
        exception = Exception('test')

        self.source.on_error(exception)

        self.assertEqual([exception], self.sink.on_error_buffer)
    def test_on_next_on_completed(self):
        init_flowable(self.source).to_rx().subscribe(self.sink,
                                                     scheduler=self.scheduler)

        self.assertEqual(self.scheduler,
                         self.source.subscriber.scheduler.underlying)

        ack = self.source.on_next_list([1, 2, 3])

        self.assertEqual([1, 2, 3], self.sink.on_next_buffer)
        self.assertIsInstance(ack, ContinueAck)

        self.source.on_completed()

        self.assertEqual([True], self.sink.on_completed_buffer)
def from_rx(
    source: rx.Observable,
    batch_size: int = None,
    overflow_strategy: OverflowStrategy = None,
    is_batched: bool = None,
) -> Flowable:
    """
    Wrap a rx.Observable and exposes it as a Flowable, relaying signals in a backpressure-aware manner.

    :param source: an rx.observable
    :param overflow_strategy: define which batches are ignored once the buffer is full
    :param batch_size: determines the number of elements that are sent in a batch
    :param is_batched: if set to True, the elements emitted by the source rx.Observable are
    either of type List or of type Iterator
    """

    if is_batched is True:
        batched_source = source
    else:
        if batch_size is None:
            batch_size = 1

        batched_source = source.pipe(operators.buffer_with_count(batch_size), )

    if isinstance(overflow_strategy, DropOld) or isinstance(
            overflow_strategy, ClearBuffer):
        return init_flowable(
            FromRxEvictingFlowable(
                batched_source=batched_source,
                overflow_strategy=overflow_strategy,
            ))

    else:
        if overflow_strategy is None:
            buffer_size = math.inf
        elif isinstance(overflow_strategy, BackPressure):
            buffer_size = overflow_strategy.buffer_size
        else:
            raise AssertionError(
                'only BackPressure is currently supported as overflow strategy'
            )

        return init_flowable(
            FromRxBufferingFlowable(
                batched_source=batched_source,
                overflow_strategy=overflow_strategy,
                buffer_size=buffer_size,
            ))
 def gen_index_for_each_deferred_state():
     """ for each value returned by the loop_flowables function """
     for key in initial_dict.keys():
         yield init_flowable(
             MapFlowable(
                 source=shared_flowable_state[key],
                 func=lambda v, key=key: (key, v)), )
def return_value(val: Any):
    """
    Create a Flowable that emits a single element.

    :param val: the single element emitted by the Flowable
    """

    return init_flowable(FromSingleElementFlowable(lazy_elem=lambda: [val], ))
Exemple #6
0
def from_list(val: List, batch_size: int = None, base: Any = None):
    """
    Create a Flowable that emits each element of the given list.

    :param val: the list whose elements are sent
    :param batch_size: determines the number of elements that are sent in a batch
    :param base: the base of the Flowable sequence
    """

    buffer = val

    if batch_size is None or len(buffer) == batch_size:

        return init_flowable(FromSingleElementFlowable(
            lazy_elem=lambda: buffer,
        ))

    else:
        if batch_size == 1:
            class EachElementIterable():
                def __iter__(self):
                    return ([e] for e in buffer)

            iterable = EachElementIterable()

        else:
            n_full_slices = int(len(buffer) / batch_size)

            class BatchIterable():
                def __iter__(self):
                    idx = 0
                    for _ in range(n_full_slices):
                        next_idx = idx + batch_size
                        yield buffer[idx:next_idx]
                        idx = next_idx

                    # if there are any elements left
                    if idx < len(buffer):
                        yield buffer[idx:]

            iterable = BatchIterable()

        return init_flowable(FromIterableFlowable(
            iterable=iterable,
        ))
Exemple #7
0
def create(observer: typing.Subscription) -> Flowable:
    """
    Create a Flowable that emits each element of the given specified subscription function.

    :param observer: Subscription function
    """
    return init_flowable(CreateFlowable(
        observer=observer,
    ))
def from_iterable(iterable: Iterable):  #, base: Any = None):
    """
    Create a Flowable that emits each element of the given iterable.

    An iterable cannot be sent in batches.

    :param iterable: the iterable whose elements are sent
    """

    return init_flowable(
        FromSingleElementFlowable(lazy_elem=lambda: iter(iterable), ))
Exemple #9
0
def interval(period: typing.RelativeTime) -> Flowable:
    """
    Create a Flowable that emits each element after each period.

    :param period: Period for producing the values in the resulting sequence
        (specified as a :class:`float` denoting seconds or an instance of
        :class:`timedelta`).
    """
    return init_flowable(IntervalFlowable(
        period=period,
    ))
    def test_immediate_yield(self):
        iterator = to_iterator(
            source=init_flowable(self.source),
            scheduler=self.scheduler,
        )

        ack = self.source.on_next_single(1)

        val = next(iterator)

        self.assertIsInstance(ack, ContinueAck)
        self.assertEqual(1, val)
def from_range(arg1: int,
               arg2: int = None,
               batch_size: int = None,
               base: Any = None):
    """
    Create a Flowable that emits elements defined by the range.

    :param arg1: start identifier
    :param arg2: end identifier
    :param batch_size: determines the number of elements that are sent in a batch
    """

    if arg2 is None:
        start_idx = 0
        stop_idx = arg1
    else:
        start_idx = arg1
        stop_idx = arg2

    n_elements = stop_idx - start_idx

    if batch_size is None:

        class FromRangeIterable:
            def __iter__(self):
                return iter(range(start_idx, stop_idx))

        iterable = FromRangeIterable()

        return from_iterable(iterable=iterable, )

    else:
        n_batches = max(math.ceil(n_elements / batch_size) - 1, 0)

        class FromRangeIterable():
            def __iter__(self):
                current_start_idx = start_idx
                current_stop_idx = start_idx

                for idx in range(n_batches):
                    current_stop_idx = current_stop_idx + batch_size

                    yield range(current_start_idx, current_stop_idx)

                    current_start_idx = current_stop_idx

                yield range(current_start_idx, stop_idx)

        iterable = FromRangeIterable()

        return init_flowable(FromIterableFlowable(iterable=iterable, ))
Exemple #12
0
    def test_use_case(self):
        sink = TObserver()
        subscription = rxbp.zip(*[init_flowable(e)
                                  for e in self.sources]).unsafe_subscribe(
                                      self.subscriber)
        subscription.observable.observe(init_observer_info(observer=sink))

        self.sources[0].observable.on_next_single(1)
        self.sources[1].observable.on_next_single(2)
        self.sources[2].observable.on_next_single(3)

        self.scheduler.advance_by(1)

        self.assertEqual([(1, 2, 3)], sink.received)
    def test_scheduled_yield(self):
        iterator = to_iterator(
            source=init_flowable(self.source),
            scheduler=self.scheduler,
        )

        def action(_, __):
            self.source.on_next_single(1)

        self.scheduler.schedule(action)

        self.scheduler.sleep(1)

        val = next(iterator)

        self.assertEqual(1, val)
Exemple #14
0
    def test_common_case(self):
        cache = to_cache(source=init_flowable(self.source), scheduler=self.scheduler)

        ack1 = self.source.on_next_single(1)
        ack2 = self.source.on_next_single(2)

        values = cache.to_list()

        ack3 = self.source.on_next_single(3)

        self.assertIsInstance(ack1, ContinueAck)
        self.assertIsInstance(ack2, ContinueAck)

        self.assertEqual([1, 2], values)

        self.assertIsInstance(ack3, StopAck)
 def test_merge(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.merge(init_flowable(self.right))
     ).unsafe_subscribe(self.subscriber)
 def _copy(self, underlying: FlowableMixin, *args, **kwargs):
     return init_flowable(underlying=underlying, )
 def test_buffer(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.buffer(10)
     ).unsafe_subscribe(self.subscriber)
 def test_default_if_empty(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.default_if_empty(lazy_val=lambda: 5),
     ).unsafe_subscribe(self.subscriber)
 def test_concat(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.concat(
             self.right
         )
     ).unsafe_subscribe(self.subscriber)
 def test_debug(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.debug('d1'),
     ).unsafe_subscribe(self.subscriber)
        def gen_conn_flowables():
            for source in self.sources:

                # buffers elements received before outgoing Flowable is subscribed
                conn_observer = ConnectableObserver(
                    underlying=None,
                    # scheduler=self.multicast_scheduler,
                )

                outer_self = self

                @dataclass
                class InnerObserver(Observer):
                    underlying: Observer
                    outer_observer: MultiCastObserver

                    def on_next(self, elem: ElementType):
                        if not outer_self.is_sent:

                            outer_self.is_sent = True
                            try:
                                observer_info.observer.on_next([flowables])
                                observer_info.observer.on_completed()
                            except Exception as exc:
                                observer_info.observer.on_error(exc)

                        return self.underlying.on_next(elem)

                    def on_error(self, err):
                        self.outer_observer.on_error(err)
                        self.underlying.on_error(err)

                    def on_completed(self):
                        self.underlying.on_completed()

                disposable = source.observe(
                    observer_info=init_multicast_observer_info(
                        observer=InnerObserver(
                            underlying=conn_observer,
                            outer_observer=observer_info.observer,
                        ), ), )

                # select a single Flowable per MultiCast
                def to_flowable(value):
                    if isinstance(value, FlowableMixin):
                        flowable = value

                    else:
                        raise Exception(f'illegal value "{value}"')

                    return flowable

                flattened_flowable = FlatConcatNoBackpressureFlowable(
                    source=ConnectableFlowable(
                        conn_observer=conn_observer,
                        disposable=disposable,
                    ),
                    selector=to_flowable,
                    subscribe_scheduler=self.source_scheduler,
                )

                # The outgoing Flowables are shared such that they can be subscribed more
                # than once
                yield init_flowable(
                    RefCountFlowable(
                        source=flattened_flowable,
                        stack=self.stack,
                    ))
def empty():
    """
    create a Flowable emitting no elements
    """

    return init_flowable(FromEmptyFlowable())
 def test_map_to_iterator(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.map_to_iterator(lambda _: [1, 2, 3])
     ).unsafe_subscribe(self.subscriber)
 def test_do_action(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.do_action(on_next=print),
     ).unsafe_subscribe(self.subscriber)
 def test_filter(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.filter(lambda v: True)
     ).unsafe_subscribe(self.subscriber)
 def test_first(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.first()
     ).unsafe_subscribe(self.subscriber)
 def test_first_or_default(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.first_or_default(lazy_val=lambda: 5)
     ).unsafe_subscribe(self.subscriber)
 def test_controlled_zip(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.controlled_zip(
             right=self.right,
         )
     ).unsafe_subscribe(self.subscriber)
 def to_flowable(self) -> Flowable[ValueType]:
     return init_flowable(FromMultiCastFlowable(
         source=self,
     ))
 def test_flat_map(self):
     subscription = init_flowable(self.left).pipe(
         rxbp.op.flat_map(lambda _: init_flowable(self.right))
     ).unsafe_subscribe(self.subscriber)