def to_future(source: Observable) -> Future: """Converts an existing observable sequence to a Future. Example: future = rx.return_value(42).pipe(ops.to_future(asyncio.Future)) Args: future_ctor: [Optional] The constructor of the future. Returns: A future with the last value from the observable sequence. """ has_value = [] def on_next(value): has_value.append(value) def on_error(err): future.set_exception(err) def on_completed(): if has_value: future.set_result(has_value.pop()) source.subscribe_(on_next, on_error, on_completed) # No cancellation can be done return future
def test_repeat_observable_repeat_count_throws(self): scheduler1 = TestScheduler() xs = Observable.return_value(1, scheduler1).repeat(3) xs.subscribe(lambda x: _raise('ex')) with self.assertRaises(RxException): scheduler1.start() scheduler2 = TestScheduler() ys = Observable.throw_exception('ex1', scheduler2).repeat(3) ys.subscribe(on_error=lambda ex: _raise('ex2')) with self.assertRaises(RxException): scheduler2.start() scheduler3 = TestScheduler() zs = Observable.return_value(1, scheduler3).repeat(100) d = zs.subscribe(on_completed=lambda: _raise('ex3')) scheduler3.schedule_absolute(10, lambda sc, st: d.dispose()) scheduler3.start() xss = Observable.create(lambda o: _raise('ex4')).repeat(3) with self.assertRaises(RxException): xss.subscribe()
def subscribe(observer): try: result = observable_factory() except Exception as ex: return Observable.throw_exception(ex).subscribe(observer) result = Observable.from_future(result) return result.subscribe(observer)
def test_zip_never_never(self): scheduler = TestScheduler() o1 = Observable.never() o2 = Observable.never() def create(): return o1.zip(o2, lambda x, y: x + y) results = scheduler.start(create) results.messages.assert_equal()
def mode(source: Observable) -> Observable: """ Returns the most frequently emitted value (or "values" if they have the same number of occurrences). The sequence must be finite. """ return source.group_by(lambda v: v) \ .flat_map(lambda grp: grp.count().map(lambda ct: (grp.key, ct))) \ .to_sorted_list(lambda t: t[1], reverse=True) \ .flat_map(lambda l: Observable.from_(l).take_while(lambda t: t[1] == l[0][1])) \ .map(lambda t: t[0])
def test_for_each_index_some_data(self): lstX = [] lstI = [] def action(x, i): lstX.append(x) lstI.append(i) Observable.range(10, 10).to_blocking().for_each(action) assert(lstX == [x for x in range(10, 20)]) assert(lstI == [x for x in range(10)])
def test_for_each_index_return(self): lstX = [] lstI = [] def action(x, i): lstX.append(x) lstI.append(i) Observable.return_value(42).to_blocking().for_each(action) assert(lstX == [42]) assert(lstI == [0])
def create(): import sys sys.setrecursionlimit(1000) def predicate(x): n[0] += 1 return n[0] < 1000 def subscribe(o): o.on_next(1) o.on_completed() return lambda: None return Observable.while_do(predicate, Observable.create(subscribe))
def do_while(self, condition): """Repeats source as long as condition holds emulating a do while loop. Keyword arguments: condition -- {Function} The condition which determines if the source will be repeated. Returns an observable {Observable} sequence which is repeated as long as the condition holds. """ return Observable.concat([self, Observable.while_do(condition, self)])
def test_when_never_never(self): scheduler = TestScheduler() xs = Observable.never() ys = Observable.never() def create(): def selector(x, y): return x + y return Observable.when(xs.and_(ys).then_do(selector)) results = scheduler.start(create) results.messages.assert_equal()
def test_return_observer_throws(self): scheduler1 = TestScheduler() xs = Observable.return_value(1, scheduler1) xs.subscribe(lambda x: _raise('ex')) self.assertRaises(RxException, scheduler1.start) scheduler2 = TestScheduler() ys = Observable.return_value(1, scheduler2) ys.subscribe(lambda x: x, lambda ex: ex, lambda: _raise('ex')) self.assertRaises(RxException, scheduler2.start)
def variance(source: ObservableBase) -> ObservableBase: """ Returns the statistical variance of the numerical emissions. The sequence must be finite. """ squared_values = source.to_list() \ .flat_map(lambda l: Observable.from_(l).average().flat_map(lambda avg: Observable.from_(l).map(lambda i: i - avg))) \ .map(lambda i: i * i) \ .publish() \ .auto_connect(2) return Observable.zip(squared_values.sum(), squared_values.count(), lambda sum, ct: sum / (ct - 1))
def repeat(self, repeat_count=None): """Repeats the observable sequence a specified number of times. If the repeat count is not specified, the sequence repeats indefinitely. 1 - repeated = source.repeat() 2 - repeated = source.repeat(42) Keyword arguments: repeat_count -- Number of times to repeat the sequence. If not provided, repeats the sequence indefinitely. Returns the observable sequence producing the elements of the given sequence repeatedly.""" return Observable.defer(lambda: Observable.concat(Enumerable.repeat(self, repeat_count)))
def merge(cls, *args): """Merges all the observable sequences into a single observable sequence. The scheduler is optional and if not specified, the immediate scheduler is used. 1 - merged = rx.Observable.merge(xs, ys, zs) 2 - merged = rx.Observable.merge([xs, ys, zs]) 3 - merged = rx.Observable.merge(scheduler, xs, ys, zs) 4 - merged = rx.Observable.merge(scheduler, [xs, ys, zs]) Returns the observable sequence that merges the elements of the observable sequences. """ if not args[0]: scheduler = immediate_scheduler sources = args[1:] elif isinstance(args[0], Scheduler): scheduler = args[0] sources = args[1:] else: scheduler = immediate_scheduler sources = args[:] if isinstance(sources[0], list): sources = sources[0] return Observable.from_(sources, scheduler).merge_all()
def some(source: Observable) -> Observable: """Partially applied operator. Determines whether some element of an observable sequence satisfies a condition if present, else if some items are in the sequence. Example: >>> obs = some(source) Args: predicate -- A function to test each element for a condition. Returns: An observable sequence containing a single element determining whether some elements in the source sequence pass the test in the specified predicate if given, else if some items are in the sequence. """ def subscribe(observer, scheduler=None): def on_next(_): observer.on_next(True) observer.on_completed() def on_error(): observer.on_next(False) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_error, scheduler) if predicate: return source.pipe( ops.filter(predicate), _some(), ) return Observable(subscribe)
def on_next(inner_source: Observable): nonlocal source d = SingleAssignmentDisposable() with source.lock: latest[0] += 1 _id = latest[0] has_latest[0] = True inner_subscription.disposable = d # Check if Future or Observable inner_source = from_future(inner_source) if is_future(inner_source) else inner_source def on_next(x: Any) -> None: if latest[0] == _id: observer.on_next(x) def on_error(e: Exception) -> None: if latest[0] == _id: observer.on_error(e) def on_completed() -> None: if latest[0] == _id: has_latest[0] = False if is_stopped[0]: observer.on_completed() d.disposable = inner_source.subscribe_(on_next, on_error, on_completed, scheduler=scheduler)
def test_for_each_index_on_next_throws(self): ex = Exception() xs = Observable.range(0, 10) def action(x, i): _raise(ex) self.assertRaises(RxException, lambda: xs.to_blocking().for_each(action))
async def go(): nonlocal result source = Observable.throw(error) try: result = await source except Exception as ex: result = ex
def create(): def subscribe(o): o.on_next(1) o.on_next(2) return Disposable.empty() return Observable.create_with_disposable(subscribe)
def test_time_interval_default_scheduler(self): import datetime xs = Observable.from_((1,2)).time_interval().pluck_attr('interval') l = [] d = xs.subscribe(lambda x: l.append(x)) self.assertEqual(len(l), 2) [self.assertIsInstance(el, datetime.timedelta) for el in l]
def create(): def subscribe(o): o.on_next(1) o.on_next(2) return lambda: None return Observable.create(subscribe)
def repeat(cls, value=None, repeat_count=None, scheduler=None): """Generates an observable sequence that repeats the given element the specified number of times, using the specified scheduler to send out observer messages. 1 - res = rx.Observable.repeat(42) 2 - res = rx.Observable.repeat(42, 4) 3 - res = rx.Observable.repeat(42, 4, Rx.Scheduler.timeout) 4 - res = rx.Observable.repeat(42, None, Rx.Scheduler.timeout) Keyword arguments: value -- Element to repeat. repeat_count -- [Optional] Number of times to repeat the element. If not specified, repeats indefinitely. scheduler -- Scheduler to run the producer loop on. If not specified, defaults to ImmediateScheduler. Returns an observable sequence that repeats the given element the specified number of times.""" scheduler = scheduler or current_thread_scheduler if repeat_count == -1: repeat_count = None xs = Observable.return_value(value, scheduler) return xs.repeat(repeat_count)
def partition(source: Observable) -> List[Observable]: """The partially applied `partition` operator. Returns two observables which partition the observations of the source by the given function. The first will trigger observations for those values for which the predicate returns true. The second will trigger observations for those values where the predicate returns false. The predicate is executed once for each subscribed observer. Both also propagate all error observations arising from the source and each completes when the source completes. Args: source: Source obserable to partition. Returns: A list of observables. The first triggers when the predicate returns True, and the second triggers when the predicate returns False. """ published = source.pipe( ops.publish(), ops.ref_count() ) return [ published.pipe(ops.filter(predicate)), published.pipe(ops.filter(lambda x: not predicate(x))) ]
def time_interval(self, scheduler): """Records the time interval between consecutive values in an observable sequence. 1 - res = source.time_interval(); 2 - res = source.time_interval(Scheduler.timeout) Keyword arguments: scheduler -- [Optional] Scheduler used to compute time intervals. If not specified, the timeout scheduler is used. Return An observable sequence with time interval information on values. """ source = self scheduler = scheduler or timeout_scheduler def defer(): last = [scheduler.now] def selector(x): now = scheduler.now span = now - last[0] last[0] = now return TimeInterval(value=x, interval=span) return source.map(selector) return Observable.defer(defer)
def test_many_select_law_1(self): xs = Observable.range(1, 0) left = xs.many_select(lambda x: x.first()) right = xs left.sequence_equal(right).first().subscribe(self.assertTrue)
def last(source: Observable) -> Observable: """Partially applied last operator. Returns the last element of an observable sequence that satisfies the condition in the predicate if specified, else the last element. Examples: >>> res = last(source) Args: source: Source observable to get last item from. Returns: An observable sequence containing the last element in the observable sequence that satisfies the condition in the predicate. """ if predicate: return source.pipe( operators.filter(predicate), operators.last() ) return last_or_default_async(source, False)
def take_until(self, other): """Returns the values from the source observable sequence until the other observable sequence produces a value. Keyword arguments: other -- Observable sequence that terminates propagation of elements of the source sequence. Returns an observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. """ source = self other = Observable.from_future(other) def subscribe(observer): def on_completed(x): observer.on_completed() return CompositeDisposable( source.subscribe(observer), other.subscribe(on_completed, observer.on_error, noop) ) return AnonymousObservable(subscribe)
def test_empty_observer_throw_exception(self): scheduler = TestScheduler() xs = Observable.empty(scheduler) xs.subscribe(lambda x: None, lambda ex: None, lambda: _raise('ex')) with self.assertRaises(RxException): scheduler.start()
def create(): def subscribe(o): d = BooleanDisposable() o.on_next(1) o.on_next(2) def action1(scheduler, state): if not d.is_disposed: o.on_next(3) scheduler.schedule_relative(600, action1) def action2(scheduler, state): if not d.is_disposed: o.on_next(4) scheduler.schedule_relative(700, action2) def action3(scheduler, state): if not d.is_disposed: o.on_next(5) scheduler.schedule_relative(900, action3) def action4(scheduler, state): if not d.is_disposed: o.on_next(6) scheduler.schedule_relative(1100, action4) return d return Observable.create_with_disposable(subscribe)
def on_next(inner_source): d = SingleAssignmentDisposable() with self.lock: latest[0] += 1 _id = latest[0] has_latest[0] = True inner_subscription.disposable = d # Check if Future or Observable inner_source = Observable.from_future(inner_source) def on_next(x): if latest[0] == _id: observer.on_next(x) def on_error(e): if latest[0] == _id: observer.on_error(e) def on_completed(): if latest[0] == _id: has_latest[0] = False if is_stopped[0]: observer.on_completed() d.disposable = inner_source.subscribe(on_next, on_error, on_completed)
def delay_subscription(source: Observable) -> Observable: """Time shifts the observable sequence by delaying the subscription. Exampeles. >>> res = source.delay_subscription(5) Args: source: Source subscription to delay. Returns: Time-shifted sequence. """ def mapper(_) -> Observable: return rx.empty() return source.pipe( ops.delay_with_mapper(rx.timer(duetime, scheduler=scheduler), mapper) )
def on_next(inner_source): if not has_current[0]: has_current[0] = True inner_source = Observable.from_future( inner_source) if is_future(inner_source) else inner_source inner_subscription = SingleAssignmentDisposable() g.add(inner_subscription) def on_completed_inner(): g.remove(inner_subscription) has_current[0] = False if is_stopped[0] and len(g) == 1: observer.on_completed() inner_subscription.disposable = inner_source.subscribe_( observer.on_next, observer.on_error, on_completed_inner, scheduler)
def flat_map_latest(source: Observable) -> Observable: """Projects each element of an observable sequence into a new sequence of observable sequences by incorporating the element's index and then transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence. Args: source: Source observable to flat map latest. Returns: An observable sequence whose elements are the result of invoking the transform function on each element of source producing an observable of Observable sequences and that at any point in time produces the elements of the most recent inner observable sequence that has been received. """ return source.pipe(ops.map(mapper), ops.switch_latest())
def interval(cls, period, scheduler=None): """Returns an observable sequence that produces a value after each period. Example: 1 - res = rx.Observable.interval(1000) 2 - res = rx.Observable.interval(1000, rx.Scheduler.timeout) Keyword arguments: period -- Period for producing the values in the resulting sequence (specified as an integer denoting milliseconds). scheduler -- [Optional] Scheduler to run the timer on. If not specified, rx.Scheduler.timeout is used. Returns an observable sequence that produces a value after each period. """ scheduler = scheduler or TimeoutScheduler() return Observable.timer(period, period, scheduler)
def skip_with_time(source: Observable) -> Observable: """Skips elements for the specified duration from the start of the observable source sequence. Args: >>> res = skip_with_time(5.0) Specifying a zero value for duration doesn't guarantee no elements will be dropped from the start of the source sequence. This is a side-effect of the asynchrony introduced by the scheduler, where the action that causes callbacks from the source sequence to be forwarded may not execute immediately, despite the zero due time. Errors produced by the source sequence are always forwarded to the result sequence, even if the error occurs before the duration. Args: duration: Duration for skipping elements from the start of the sequence. Returns: An observable sequence with the elements skipped during the specified duration from the start of the source sequence. """ def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or timeout_scheduler open = [False] def action(scheduler, state): open[0] = True t = _scheduler.schedule_relative(duration, action) def on_next(x): if open[0]: observer.on_next(x) d = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler_) return CompositeDisposable(t, d) return Observable(subscribe)
def action(scheduler, state=None): try: source = next(sources) except StopIteration: observer.on_completed() return # Allow source to be a factory method taking an error source = source(state) if callable(source) else source current = Observable.from_future(source) d = SingleAssignmentDisposable() subscription.disposable = d def on_resume(state=None): scheduler.schedule(action, state) d.disposable = current.subscribe(observer.on_next, on_resume, on_resume)
def skip_until(self, other): """Returns the values from the source observable sequence only after the other observable sequence produces a value. other -- The observable sequence that triggers propagation of elements of the source sequence. Returns an observable sequence containing the elements of the source sequence starting from the point the other sequence triggered propagation. """ source = self other = Observable.from_future(other) def subscribe(observer): is_open = [False] def on_next(left): if is_open[0]: observer.on_next(left) def on_completed(): if is_open[0]: observer.on_completed() subs = source.subscribe(on_next, observer.on_error, on_completed) disposables = CompositeDisposable(subs) right_subscription = SingleAssignmentDisposable() disposables.add(right_subscription) def on_next2(x): is_open[0] = True right_subscription.dispose() def on_completed2(): right_subscription.dispose() right_subscription.disposable = other.subscribe(on_next2, observer.on_error, on_completed2) return disposables return AnonymousObservable(subscribe)
def from_iterable(iterable: Iterable, scheduler: Optional[typing.Scheduler] = None) -> Observable: """Converts an iterable to an observable sequence. Example: >>> from_iterable([1,2,3]) Args: iterable: A Python iterable scheduler: An optional scheduler to schedule the values on. Returns: The observable sequence whose elements are pulled from the given iterable sequence. """ def subscribe( observer: typing.Observer, scheduler_: Optional[typing.Scheduler] = None ) -> typing.Disposable: _scheduler = scheduler or scheduler_ or current_thread_scheduler iterator = iter(iterable) disposed = False def action(_: typing.Scheduler, __: Any = None) -> None: nonlocal disposed try: while not disposed: value = next(iterator) observer.on_next(value) except StopIteration: observer.on_completed() except Exception as error: # pylint: disable=broad-except observer.on_error(error) def dispose() -> None: nonlocal disposed disposed = True disp = Disposable(dispose) return CompositeDisposable(_scheduler.schedule(action), disp) return Observable(subscribe)
def _concat_with_iterable(sources: Iterable[Observable]) -> Observable: sources_ = iter(sources) def subscribe(observer, scheduler=None): scheduler = scheduler or CurrentThreadScheduler.singleton() subscription = SerialDisposable() cancelable = SerialDisposable() is_disposed = False def action(action1, state=None): nonlocal is_disposed if is_disposed: return def on_completed(): cancelable.disposable = scheduler.schedule(action) try: current = next(sources_) except StopIteration: observer.on_completed() except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) else: d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = current.subscribe_(observer.on_next, observer.on_error, on_completed, scheduler) cancelable.disposable = scheduler.schedule(action) def dispose(): nonlocal is_disposed is_disposed = True return CompositeDisposable(subscription, cancelable, Disposable(dispose)) return Observable(subscribe)
def window_with_count(source: Observable) -> Observable: def subscribe(observer, scheduler=None): m = SingleAssignmentDisposable() refCountDisposable = RefCountDisposable(m) n = [0] q = [] def create_window(): s = Subject() q.append(s) observer.on_next(add_ref(s, refCountDisposable)) create_window() def on_next(x): for item in q: item.on_next(x) c = n[0] - count + 1 if c >= 0 and c % skip == 0: s = q.pop(0) s.on_completed() n[0] += 1 if (n[0] % skip) == 0: create_window() def on_error(exception): while q: q.pop(0).on_error(exception) observer.on_error(exception) def on_completed(): while q: q.pop(0).on_completed() observer.on_completed() m.disposable = source.subscribe_(on_next, on_error, on_completed, scheduler) return refCountDisposable return Observable(subscribe)
def extrema_by(source: Observable, key_mapper: Mapper, comparer: Comparer ) -> Observable: def subscribe(observer, scheduler=None): has_value = [False] last_key = [None] items = [] def on_next(x): try: key = key_mapper(x) except Exception as ex: observer.on_error(ex) return comparison = 0 if not has_value[0]: has_value[0] = True last_key[0] = key else: try: comparison = comparer(key, last_key[0]) except Exception as ex1: observer.on_error(ex1) return if comparison > 0: last_key[0] = key items[:] = [] if comparison >= 0: items.append(x) def on_completed(): observer.on_next(items) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) return Observable(subscribe)
def take_while(source: Observable) -> Observable: """Returns elements from an observable sequence as long as a specified condition is true. Example: >>> take_while(source) Args: source: The source observable to take from. Returns: An observable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes. """ def subscribe(observer, scheduler=None): running = True def on_next(value): nonlocal running with source.lock: if not running: return try: running = predicate(value) except Exception as exn: observer.on_error(exn) return if running: observer.on_next(value) else: if inclusive: observer.on_next(value) observer.on_completed() return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) return Observable(subscribe)
def delay_subscription(self, duetime, scheduler=None): """Time shifts the observable sequence by delaying the subscription. 1 - res = source.delay_subscription(5000) # 5s 2 - res = source.delay_subscription(5000, Scheduler.timeout) # 5 seconds duetime -- Absolute or relative time to perform the subscription at. scheduler [Optional] Scheduler to run the subscription delay timer on. If not specified, the timeout scheduler is used. Returns time-shifted sequence. """ scheduler = scheduler or timeout_scheduler def selector(_): return Observable.empty() return self.delay_with_selector(Observable.timer(duetime, scheduler), selector)
def last_or_default_async(source, has_default=False, default_value=None): def subscribe(observer, scheduler=None): value = [default_value] seen_value = [False] def on_next(x): value[0] = x seen_value[0] = True def on_completed(): if not seen_value[0] and not has_default: observer.on_error(SequenceContainsNoElementsError()) else: observer.on_next(value[0]) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) return Observable(subscribe)
def repeat_value(value: Any = None, repeat_count: int = None) -> ObservableBase: """Generates an observable sequence that repeats the given element the specified number of times. 1 - res = repeat_value(42) 2 - res = repeat_value(42, 4) Keyword arguments: value -- Element to repeat. repeat_count -- [Optional] Number of times to repeat the element. If not specified, repeats indefinitely. Returns an observable sequence that repeats the given element the specified number of times.""" if repeat_count == -1: repeat_count = None xs = Observable.return_value(value) return xs.repeat(repeat_count)
def _with_latest_from(parent: Observable, *sources: Observable) -> Observable: NO_VALUE = NotSet() def subscribe(observer, scheduler=None): def subscribe_all(parent, *children): values = [NO_VALUE for _ in children] def subscribe_child(i, child): subscription = SingleAssignmentDisposable() def on_next(value): with parent.lock: values[i] = value subscription.disposable = child.subscribe_(on_next, observer.on_error, scheduler=scheduler) return subscription parent_subscription = SingleAssignmentDisposable() def on_next(value): with parent.lock: if NO_VALUE not in values: result = (value, ) + tuple(values) observer.on_next(result) disp = parent.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) parent_subscription.disposable = disp children_subscription = [ subscribe_child(i, child) for i, child in enumerate(children) ] return [parent_subscription] + children_subscription return CompositeDisposable(subscribe_all(parent, *sources)) return Observable(subscribe)
def to_dict(source: Observable) -> Observable: """Converts the observable sequence to a Map if it exists. Args: source: Source observable to convert. Returns: An observable sequence with a single value of a dictionary containing the values from the observable sequence. """ def subscribe( observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None ) -> typing.Disposable: m = dict() def on_next(x: Any) -> None: try: key = key_mapper(x) except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) return element = x if element_mapper: try: element = element_mapper(x) except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) return m[key] = element def on_completed() -> None: observer.on_next(m) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) return Observable(subscribe)
def _generate(initial_state: Any, condition: Predicate, iterate: Mapper ) -> Observable: def subscribe(observer, scheduler=None): scheduler = scheduler or CurrentThreadScheduler.singleton() first = True state = initial_state mad = MultipleAssignmentDisposable() def action(scheduler, state1=None): nonlocal first nonlocal state has_result = False result = None try: if first: first = False else: state = iterate(state) has_result = condition(state) if has_result: result = state except Exception as exception: # pylint: disable=broad-except observer.on_error(exception) return if has_result: observer.on_next(result) mad.disposable = scheduler.schedule(action) else: observer.on_completed() mad.disposable = scheduler.schedule(action) return mad return Observable(subscribe)
def observe_on(source: Observable) -> Observable: """Wraps the source sequence in order to run its observer callbacks on the specified scheduler. This only invokes observer callbacks on a scheduler. In case the subscription and/or unsubscription actions have side-effects that require to be run on a scheduler, use subscribe_on. Args: source: Source observable. Returns: Returns the source sequence whose observations happen on the specified scheduler. """ def subscribe(observer, _=None): return source.subscribe(ObserveOnObserver(scheduler, observer)) return Observable(subscribe)
def last_or_default(source: Observable) -> Observable: """Return last or default element. Examples: >>> res = _last_or_default(source) Args: source: Observable sequence to get the last item from. Returns: Observable sequence containing the last element in the observable sequence. """ if predicate: return source.pipe( ops.filter(predicate), ops.last_or_default(None, default_value), ) return last_or_default_async(source, True, default_value)
def test_map_throws(self): with self.assertRaises(RxException): Observable.return_value(1) \ .map(mapper_indexed=lambda x, y: x) \ .subscribe_(lambda x: _raise("ex")) with self.assertRaises(RxException): Observable.throw('ex') \ .map(mapper_indexed=lambda x, y: x) \ .subscribe_(on_error=lambda ex: _raise(ex)) with self.assertRaises(RxException): Observable.empty() \ .map(mapper_indexed=lambda x, y: x) \ .subscribe_(lambda x: x, lambda ex: ex, lambda: _raise('ex')) def subscribe(observer, scheduler=None): _raise('ex') with self.assertRaises(RxException): Observable.create(subscribe) \ .map(lambda x: x) \ .subscribe()
def test_select_throws(self): with self.assertRaises(RxException): Observable.return_value(1) \ .map(lambda x, y: x) \ .subscribe(lambda x: _raise("ex")) with self.assertRaises(RxException): Observable.throw_exception('ex') \ .map(lambda x, y: x) \ .subscribe(on_error=lambda ex: _raise(ex)) with self.assertRaises(RxException): Observable.empty() \ .map(lambda x, y: x) \ .subscribe(lambda x: x, lambda ex: ex, lambda: _raise('ex')) def subscribe(observer): _raise('ex') with self.assertRaises(RxException): Observable.create(subscribe) \ .map(lambda x: x) \ .subscribe()
def many_select(self, selector, scheduler=None): """Comonadic bind operator. Internally projects a new observable for each value, and it pushes each observable into the user-defined selector function that projects/queries each observable into some result. Keyword arguments: selector -- {Function} A transform function to apply to each element. scheduler -- {Object} [Optional] Scheduler used to execute the operation. If not specified, defaults to the ImmediateScheduler. Returns {Observable} An observable sequence which results from the comonadic bind operation. """ scheduler = scheduler or immediate_scheduler source = self def factory(): chain = [None] def mapper(x): curr = ChainObservable(x) chain[0] and chain[0].on_next(x) chain[0] = curr return curr def on_error(e): if chain[0]: chain[0].on_error(e) def on_completed(): if chain[0]: chain[0].on_completed() return source.map(mapper).tap( noop, on_error, on_completed).observe_on(scheduler).map(selector) return Observable.defer(factory)
def exclusive(source: Observable) -> Observable: def subscribe(observer, scheduler=None): has_current = [False] is_stopped = [False] m = SingleAssignmentDisposable() g = CompositeDisposable() g.add(m) def on_next(inner_source): if not has_current[0]: has_current[0] = True inner_source = rx.from_future(inner_source) if is_future( inner_source) else inner_source inner_subscription = SingleAssignmentDisposable() g.add(inner_subscription) def on_completed_inner(): g.remove(inner_subscription) has_current[0] = False if is_stopped[0] and len(g) == 1: observer.on_completed() inner_subscription.disposable = inner_source.subscribe_( observer.on_next, observer.on_error, on_completed_inner, scheduler) def on_completed(): is_stopped[0] = True if not has_current[0] and len(g) == 1: observer.on_completed() m.disposable = source.subscribe_(on_next, observer.on_error, on_completed, scheduler) return g return Observable(subscribe)
def test_ref_count_notconnected(self): disconnected = [False] count = [0] def factory(scheduler): count[0] += 1 def create(obs): def func(): disconnected[0] = True return func return Observable.create(create) xs = Observable.defer(factory) subject = MySubject() conn = ConnectableObservable(xs, subject) refd = conn.ref_count() dis1 = refd.subscribe() self.assertEqual(1, count[0]) self.assertEqual(1, subject.subscribe_count) assert (not disconnected[0]) dis2 = refd.subscribe() self.assertEqual(1, count[0]) self.assertEqual(2, subject.subscribe_count) assert (not disconnected[0]) dis1.dispose() assert (not disconnected[0]) dis2.dispose() assert (disconnected[0]) disconnected[0] = False dis3 = refd.subscribe() self.assertEqual(2, count[0]) self.assertEqual(3, subject.subscribe_count) assert (not disconnected[0]) dis3.dispose() assert (disconnected[0])
def catch_exception(self, second=None, handler=None): """Continues an observable sequence that is terminated by an exception with the next observable sequence. 1 - xs.catch_exception(ys) 2 - xs.catch_exception(lambda ex: ys(ex)) Keyword arguments: handler -- Exception handler function that returns an observable sequence given the error that occurred in the first sequence. second -- Second observable sequence used to produce results when an error occurred in the first sequence. Returns an observable sequence containing the first sequence's elements, followed by the elements of the handler sequence in case an exception occurred. """ if handler or not isinstance(second, Observable): return catch_handler(self, handler or second) return Observable.catch_exception([self, second])
def with_latest_from(self, *args): """Merges the specified observable sequences into one observable sequence by using the selector function only when the source observable sequence (the instance) produces an element. The other observables can be passed either as seperate arguments or as a list. 1 - obs = observable.with_latest_from(obs1, obs2, obs3, lambda o1, o2, o3: o1 + o2 + o3) 2 - obs = observable.with_latest_from([obs1, obs2, obs3], lambda o1, o2, o3: o1 + o2 + o3) Returns an observable sequence containing the result of combining elements of the sources using the specified result selector function. """ args = list(args) if args and isinstance(args[0], list): args = args[0] args.insert(0, self) return Observable.with_latest_from(*args)
def take_until(source: Observable) -> Observable: """Returns the values from the source observable sequence until the other observable sequence produces a value. Args: source: The source observable sequence. Returns: An observable sequence containing the elements of the source sequence up to the point the other sequence interrupted further propagation. """ def subscribe(observer, scheduler=None): def on_completed(_): observer.on_completed() return CompositeDisposable( source.subscribe(observer), other.subscribe_(on_completed, observer.on_error, noop, scheduler)) return Observable(subscribe)
def amb(*args): """Propagates the observable sequence that reacts first. E.g. winner = Observable.amb(xs, ys, zs) Returns an observable sequence that surfaces any of the given sequences, whichever reacted first. """ acc = Observable.never() if isinstance(args[0], list): items = args[0] else: items = list(args) def func(previous, current): return _amb(previous, current) for item in items: acc = func(acc, item) return acc
def do_on_dispose(source: Observable, on_dispose): """Invokes an action on disposal. This can be helpful for debugging, logging, and other side effects on the disposal of an operation. Args: on_dispose: Action to invoke on disposal """ class OnDispose(Disposable): def dispose(self): on_dispose() def subscribe(observer, scheduler=None): composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose()) subscription = source.subscribe_(observer.on_next, observer.on_error, observer.on_completed, scheduler) composite_disposable.add(subscription) return composite_disposable return Observable(subscribe)