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], ))
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, ))
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), ))
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, ))
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)
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)