def _using(resource_factory: Callable[[], typing.Disposable], observable_factory: Callable[[typing.Disposable], Observable] ) -> Observable: """Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime. Example: >>> res = rx.using(lambda: AsyncSubject(), lambda: s: s) Args: resource_factory: Factory function to obtain a resource object. observable_factory: Factory function to obtain an observable sequence that depends on the obtained resource. Returns: An observable sequence whose lifetime controls the lifetime of the dependent resource object. """ def subscribe(observer, scheduler=None): disp = Disposable() try: resource = resource_factory() if resource is not None: disp = resource source = observable_factory(resource) except Exception as exception: # pylint: disable=broad-except d = rx.throw(exception).subscribe(observer, scheduler=scheduler) return CompositeDisposable(d, disp) return CompositeDisposable(source.subscribe(observer, scheduler=scheduler), disp) return Observable(subscribe)
def add_ref(xs, r): from rx.core import Observable def subscribe(observer, scheduler=None): return CompositeDisposable(r.disposable, xs.subscribe(observer)) return Observable(subscribe)
def map(source: Observable) -> Observable: """Partially applied map operator. Project each element of an observable sequence into a new form by incorporating the element's index. Example: >>> map(source) Args: source: The observable source to transform. Returns: Returns an observable sequence whose elements are the result of invoking the transform function on each element of the source. """ def subscribe(obv: Observer, scheduler: Scheduler = None) -> Disposable: def on_next(value: Any) -> None: try: result = _mapper(value) except Exception as err: # pylint: disable=broad-except obv.on_error(err) else: obv.on_next(result) return source.subscribe_(on_next, obv.on_error, obv.on_completed, scheduler) return Observable(subscribe)
def time_interval(source: Observable) -> Observable: """Records the time interval between consecutive values in an observable sequence. >>> res = time_interval(source) Return: An observable sequence with time interval information on values. """ def subscribe(observer, scheduler_): _scheduler = scheduler or scheduler_ or timeout_scheduler last = _scheduler.now def mapper(value): nonlocal last now = _scheduler.now span = now - last last = now return TimeInterval(value=value, interval=span) return source.pipe(ops.map(mapper)).subscribe(observer, scheduler_) return Observable(subscribe)
def element_at_or_default(source: Observable) -> Observable: def subscribe(observer, scheduler=None): i = [index] def on_next(x): found = False with source.lock: if i[0]: i[0] -= 1 else: found = True if found: observer.on_next(x) observer.on_completed() def on_completed(): if not has_default: observer.on_error(ArgumentOutOfRangeException()) else: observer.on_next(default_value) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) return Observable(subscribe)
def _from_future(future: Future) -> Observable: """Converts a Future to an Observable sequence Args: future -- A Python 3 compatible future. https://docs.python.org/3/library/asyncio-task.html#future http://www.tornadoweb.org/en/stable/concurrent.html#tornado.concurrent.Future Returns: An Observable sequence which wraps the existing future success and failure. """ def subscribe( observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None) -> typing.Disposable: def done(future): try: value = future.result() except Exception as ex: observer.on_error(ex) else: observer.on_next(value) observer.on_completed() future.add_done_callback(done) def dispose() -> None: if future and future.cancel: future.cancel() return Disposable(dispose) return Observable(subscribe)
def _return_value(value: Any, scheduler: typing.Scheduler = None) -> Observable: """Returns an observable sequence that contains a single element, using the specified scheduler to send out observer messages. There is an alias called 'just'. Examples: >>> res = return(42) >>> res = return(42, rx.Scheduler.timeout) Args: value: Single element in the resulting observable sequence. Returns: An observable sequence containing the single specified element. """ def subscribe(observer: typing.Observer, scheduler_: typing.Scheduler = None) -> typing.Disposable: _scheduler = scheduler or scheduler_ or current_thread_scheduler def action(scheduler: typing.Scheduler, state: Any = None): observer.on_next(value) observer.on_completed() return _scheduler.schedule(action) return Observable(subscribe)
def skip_last(source: Observable) -> Observable: """Bypasses a specified number of elements at the end of an observable sequence. This operator accumulates a queue with a length enough to store the first `count` elements. As more elements are received, elements are taken from the front of the queue and produced on the result sequence. This causes elements to be delayed. Args: count: Number of elements to bypass at the end of the source sequence. Returns: An observable sequence containing the source sequence elements except for the bypassed ones at the end. """ def subscribe(observer, scheduler=None): q = [] def on_next(value): front = None with source.lock: q.append(value) if len(q) > count: front = q.pop(0) if front is not None: observer.on_next(front) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) return Observable(subscribe)
def materialize(source: Observable) -> Observable: """Partially applied materialize operator. Materializes the implicit notifications of an observable sequence as explicit notification values. Args: source: Source observable to materialize. Returns: An observable sequence containing the materialized notification values from the source sequence. """ def subscribe(observer, scheduler=None): def on_next(value): observer.on_next(OnNext(value)) def on_error(exception): observer.on_next(OnError(exception)) observer.on_completed() def on_completed(): observer.on_next(OnCompleted()) observer.on_completed() return source.subscribe_(on_next, on_error, on_completed, scheduler) return Observable(subscribe)
def _generate(initial_state, condition, iterate) -> Observable: def subscribe(observer, scheduler=None): scheduler = scheduler or current_thread_scheduler first = [True] state = [initial_state] mad = MultipleAssignmentDisposable() def action(scheduler, state1=None): has_result = False result = None try: if first[0]: first[0] = False else: state[0] = iterate(state[0]) has_result = condition(state[0]) if has_result: result = state[0] 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 function(*args): arguments = list(args) def subscribe(observer: typing.Observer, scheduler: typing.Scheduler = None) -> typing.Disposable: def handler(*args): results = list(args) if mapper: try: results = mapper(args) except Exception as err: # pylint: disable=broad-except observer.on_error(err) return observer.on_next(results) else: if isinstance(results, list) and len(results) <= 1: observer.on_next(*results) else: observer.on_next(results) observer.on_completed() arguments.append(handler) func(*arguments) return Disposable() return Observable(subscribe)
def skip_until(source: Observable) -> Observable: def subscribe(observer, scheduler=None): 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, scheduler) subscriptions = CompositeDisposable(subs) right_subscription = SingleAssignmentDisposable() subscriptions.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, scheduler) return subscriptions return Observable(subscribe)
def default_if_empty(source: Observable) -> Observable: """Returns the elements of the specified sequence or the specified value in a singleton sequence if the sequence is empty. Examples: >>> obs = default_if_empty(source) Args: source: Source observable. Returns: An observable sequence that contains the specified default value if the source is empty otherwise, the elements of the source. """ def subscribe(observer, scheduler=None) -> Disposable: found = [False] def on_next(x: Any): found[0] = True observer.on_next(x) def on_completed(): if not found[0]: observer.on_next(default_value) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler) return Observable(subscribe)
def take(source: Observable) -> Observable: """Returns a specified number of contiguous elements from the start of an observable sequence. >>> take(source) Keyword arguments: count -- The number of elements to return. Returns an observable sequence that contains the specified number of elements from the start of the input sequence. """ if not count: return empty() def subscribe(observer, scheduler=None): remaining = count def on_next(value): nonlocal remaining if remaining > 0: remaining -= 1 observer.on_next(value) if not remaining: observer.on_completed() return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) return Observable(subscribe)
def window(source: Observable) -> Observable: def subscribe(observer, scheduler=None): window_subject = Subject() d = CompositeDisposable() r = RefCountDisposable(d) observer.on_next(add_ref(window_subject, r)) def on_next_window(x): window_subject.on_next(x) def on_error(err): window_subject.on_error(err) observer.on_error(err) def on_completed(): window_subject.on_completed() observer.on_completed() d.add( source.subscribe_(on_next_window, on_error, on_completed, scheduler)) def on_next_observer(w): nonlocal window_subject window_subject.on_completed() window_subject = Subject() observer.on_next(add_ref(window_subject, r)) d.add( boundaries.subscribe_(on_next_observer, on_error, on_completed, scheduler)) return r return Observable(subscribe)
def take_until_with_time(source: Observable) -> Observable: """Takes elements for the specified duration until the specified end time, using the specified scheduler to run timers. Examples: >>> res = take_until_with_time(source) Args: source: Source observale to take elements from. Returns: An observable sequence with the elements taken until the specified end time. """ def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or timeout_scheduler if isinstance(end_time, datetime): scheduler_method = _scheduler.schedule_absolute else: scheduler_method = _scheduler.schedule_relative def action(scheduler, state): observer.on_completed() task = scheduler_method(end_time, action) return CompositeDisposable(task, source.subscribe(observer, scheduler=scheduler_)) return Observable(subscribe)
def take_with_time(source: Observable) -> Observable: """Takes elements for the specified duration from the start of the observable source sequence. Example: >>> res = take_with_time(source) This operator accumulates a queue with a length enough to store elements received during the initial duration window. As more elements are received, elements older than the specified duration are taken from the queue and produced on the result sequence. This causes elements to be delayed with duration. Args: source: Source observable to take elements from. Returns: An observable sequence with the elements taken during the specified duration from the start of the source sequence. """ def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton( ) def action(scheduler, state): observer.on_completed() disp = _scheduler.schedule_relative(duration, action) return CompositeDisposable( disp, source.subscribe(observer, scheduler=scheduler_)) return Observable(subscribe)
def finally_action(source: Observable) -> Observable: """Invokes a specified action after the source observable sequence terminates gracefully or exceptionally. Example: res = finally(source) Args: source: Observable sequence. Returns: An observable sequence with the action-invoking termination behavior applied. """ def subscribe(observer, scheduler=None): try: subscription = source.subscribe(observer, scheduler=scheduler) except Exception: action() raise def dispose(): try: subscription.dispose() finally: action() return Disposable(dispose) return Observable(subscribe)
def _defer( factory: Callable[[Scheduler], Union[Observable, Future]]) -> Observable: """Returns an observable sequence that invokes the specified factory function whenever a new observer subscribes. Example: >>> res = defer(lambda: of(1, 2, 3)) Args: observable_factory: Observable factory function to invoke for each observer that subscribes to the resulting sequence. Returns: An observable sequence whose observers trigger an invocation of the given observable factory function. """ def subscribe(observer, scheduler=None): try: result = factory(scheduler) except Exception as ex: # By design. pylint: disable=W0703 return throw(ex).subscribe(observer) result = from_future(result) if is_future(result) else result return result.subscribe(observer, scheduler=scheduler) return Observable(subscribe)
def do_after_terminate(source, after_terminate): """Invokes an action after an on_complete() or on_error() event. This can be helpful for debugging, logging, and other side effects when completion or an error terminates an operation on_terminate -- Action to invoke after on_complete or throw is called """ def subscribe(observer, scheduler=None): def on_completed(): observer.on_completed() try: after_terminate() except Exception as err: # pylint: disable=broad-except observer.on_error(err) def on_error(exception): observer.on_error(exception) try: after_terminate() except Exception as err: # pylint: disable=broad-except observer.on_error(err) return source.subscribe(observer.on_next, on_error, on_completed, scheduler) return Observable(subscribe)
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 partial(source: Observable) -> Observable: def subscribe(observer, scheduler=None): was_invoked = [False] def on_completed(): observer.on_completed() try: if not was_invoked[0]: finally_action() was_invoked[0] = True except Exception as err: # pylint: disable=broad-except observer.on_error(err) def on_error(exception): observer.on_error(exception) try: if not was_invoked[0]: finally_action() was_invoked[0] = True except Exception as err: # pylint: disable=broad-except observer.on_error(err) composite_disposable = CompositeDisposable() composite_disposable.add(OnDispose(was_invoked)) subscription = source.subscribe_(observer.on_next, on_error, on_completed, scheduler) composite_disposable.add(subscription) return composite_disposable return Observable(subscribe)
def _with_latest_from(parent: Observable, *sources: Observable) -> Observable: NO_VALUE = object() 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 skip(source: Observable) -> Observable: """The skip operator. Bypasses a specified number of elements in an observable sequence and then returns the remaining elements. Args: source: The source observable. Returns: An observable sequence that contains the elements that occur after the specified index in the input sequence. """ def subscribe(observer, scheduler=None): remaining = count def on_next(value): nonlocal remaining if remaining <= 0: observer.on_next(value) else: remaining -= 1 return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) return Observable(subscribe)
def throttle_first(source: Observable) -> Observable: """Returns an observable that emits only the first item emitted by the source Observable during sequential time windows of a specifiedduration. Args: source: Source observable to throttle. Returns: An Observable that performs the throttle operation. """ def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or timeout_scheduler duration = _scheduler.to_timedelta(window_duration or 0.0) if duration <= _scheduler.to_timedelta(0): raise ValueError('window_duration cannot be less or equal zero.') last_on_next = [0] def on_next(x): emit = False now = _scheduler.now with source.lock: if not last_on_next[0] or now - last_on_next[0] >= duration: last_on_next[0] = now emit = True if emit: observer.on_next(x) return source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler=_scheduler) return Observable(subscribe)
def skip_last_with_time(source: Observable) -> Observable: def subscribe(observer, scheduler_=None): nonlocal duration _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton( ) duration = _scheduler.to_timedelta(duration) q = [] def on_next(x): now = _scheduler.now q.append({"interval": now, "value": x}) while q and now - q[0]["interval"] >= duration: observer.on_next(q.pop(0)["value"]) def on_completed(): now = _scheduler.now while q and now - q[0]["interval"] >= duration: observer.on_next(q.pop(0)["value"]) observer.on_completed() return source.subscribe_(on_next, observer.on_error, on_completed, scheduler_) return Observable(subscribe)
def catch_handler( source: Observable, handler: Callable[[Exception, Observable], Observable]) -> Observable: def subscribe(observer, scheduler=None): d1 = SingleAssignmentDisposable() subscription = SerialDisposable() subscription.disposable = d1 def on_error(exception): try: result = handler(exception, source) except Exception as ex: # By design. pylint: disable=W0703 observer.on_error(ex) return result = rx.from_future(result) if is_future(result) else result d = SingleAssignmentDisposable() subscription.disposable = d d.disposable = result.subscribe(observer, scheduler=scheduler) d1.disposable = source.subscribe_(observer.on_next, on_error, observer.on_completed, scheduler) return subscription return Observable(subscribe)
def observable_timer_duetime_and_period( duetime, period, scheduler: Optional[typing.Scheduler] = None) -> Observable: def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or timeout_scheduler nonlocal duetime if not isinstance(duetime, datetime): duetime = _scheduler.now + _scheduler.to_timedelta(duetime) p = _scheduler.normalize(period) mad = MultipleAssignmentDisposable() dt = [duetime] count = [0] def action(scheduler, state): if p > 0: now = scheduler.now dt[0] = dt[0] + scheduler.to_timedelta(p) if dt[0] <= now: dt[0] = now + scheduler.to_timedelta(p) observer.on_next(count[0]) count[0] += 1 mad.disposable = scheduler.schedule_absolute(dt[0], action) mad.disposable = _scheduler.schedule_absolute(dt[0], action) return mad return Observable(subscribe)
def observable_timer_duetime_and_period( duetime, period, scheduler: Optional[typing.Scheduler] = None) -> Observable: def subscribe(observer, scheduler_=None): _scheduler = scheduler or scheduler_ or TimeoutScheduler.singleton() nonlocal duetime if not isinstance(duetime, datetime): duetime = _scheduler.now + _scheduler.to_timedelta(duetime) p = max(0.0, _scheduler.to_seconds(period)) mad = MultipleAssignmentDisposable() dt = duetime count = 0 def action(scheduler, state): nonlocal dt nonlocal count if p > 0.0: now = scheduler.now dt = dt + scheduler.to_timedelta(p) if dt <= now: dt = now + scheduler.to_timedelta(p) observer.on_next(count) count += 1 mad.disposable = scheduler.schedule_absolute(dt, action) mad.disposable = _scheduler.schedule_absolute(dt, action) return mad return Observable(subscribe)
def _zip(*args: Observable) -> Observable: """Merges the specified observable sequences into one observable sequence by creating a tuple whenever all of the observable sequences have produced an element at a corresponding index. Example: >>> res = zip(obs1, obs2) Args: args: Observable sources to zip. Returns: An observable sequence containing the result of combining elements of the sources as tuple. """ sources = list(args) def subscribe( observer: typing.Observer, scheduler: Optional[typing.Scheduler] = None ) -> CompositeDisposable: n = len(sources) queues: List[List] = [[] for _ in range(n)] lock = RLock() @synchronized(lock) def next(i): if all([len(q) for q in queues]): try: queued_values = [x.pop(0) for x in queues] res = tuple(queued_values) except Exception as ex: # pylint: disable=broad-except observer.on_error(ex) return observer.on_next(res) subscriptions = [None] * n def func(i): source = sources[i] sad = SingleAssignmentDisposable() source = from_future(source) if is_future(source) else source def on_next(x): queues[i].append(x) next(i) sad.disposable = source.subscribe_(on_next, observer.on_error, observer.on_completed, scheduler) subscriptions[i] = sad for idx in range(n): func(idx) return CompositeDisposable(subscriptions) return Observable(subscribe)