def on_next(notification): log.debug("observable_delay_timespan:subscribe:on_next()") should_run = False with source.lock: if notification.value.kind == 'E': del queue[:] queue.append(notification) exception[0] = notification.value.exception should_run = not running[0] else: queue.append(Timestamp(value=notification.value, timestamp=notification.timestamp + duetime)) should_run = not active[0] active[0] = True if should_run: if exception[0]: log.error("*** Exception: %s", exception[0]) observer.on_error(exception[0]) else: mad = MultipleAssignmentDisposable() cancelable.disposable = mad def action(scheduler, state): if exception[0]: log.error("observable_delay_timespan:subscribe:on_next:action(), exception: %s", exception[0]) return with source.lock: running[0] = True while True: result = None if len(queue) and queue[0].timestamp <= scheduler.now: result = queue.pop(0).value if result: result.accept(observer) if not result: break should_continue = False recurse_duetime = 0 if len(queue): should_continue = True diff = queue[0].timestamp - scheduler.now zero = timedelta(0) if isinstance(diff, timedelta) else 0 recurse_duetime = max(zero, diff) else: active[0] = False ex = exception[0] running[0] = False if ex: observer.on_error(ex) elif should_continue: mad.disposable = scheduler.schedule_relative(recurse_duetime, action) mad.disposable = scheduler.schedule_relative(duetime, action)
def schedule_periodic(self, period, action, state=None): """Schedules a periodic piece of work to be executed in the tkinter mainloop. Keyword arguments: period -- Period in milliseconds for running the work periodically. action -- Action to be executed. state -- [Optional] Initial state passed to the action upon the first iteration. Returns the disposable object used to cancel the scheduled recurring action (best effort).""" disposable = MultipleAssignmentDisposable() state = [state] def invoke_action(scheduler, _): if disposable.is_disposed: return new_state = action(state[0]) if new_state is not None: state[0] = new_state disposable.disposable = self.schedule_relative(period, invoke_action, state) disposable.disposable = self.schedule_relative(period, invoke_action, state) return disposable
def subscribe(observer): 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 = result_selector(state[0]) except Exception as exception: 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
def subscribe(observer): mad = MultipleAssignmentDisposable() state = [initial_state] has_result = [False] result = [None] first = [True] time = [None] def action(scheduler, _): if has_result[0]: observer.on_next(result[0]) try: if first[0]: first[0] = False else: state[0] = iterate(state[0]) has_result[0] = condition(state[0]) if has_result[0]: result[0] = result_selector(state[0]) time[0] = time_selector(state[0]) except Exception as e: observer.on_error(e) return if has_result[0]: mad.disposable = scheduler.schedule_relative(time[0], action) else: observer.on_completed() mad.disposable = scheduler.schedule_relative(0, action) return mad
def subscribe(observer): sd = MultipleAssignmentDisposable() def action(scheduler, n): if n < end: observer.on_next(n) sd.disposable = scheduler.schedule(action, n + 1) else: observer.on_completed() sd.disposable = scheduler.schedule(action, start) return sd
def subscribe(observer, scheduler=None): nonlocal range_t scheduler = scheduler or current_thread_scheduler sd = MultipleAssignmentDisposable() def action(scheduler, iterator): try: observer.on_next(next(iterator)) sd.disposable = scheduler.schedule(action, state=iterator) except StopIteration: observer.on_completed() sd.disposable = scheduler.schedule(action, iter(range_t)) return sd
def range(cls, start, count, scheduler=None): """Generates an observable sequence of integral numbers within a specified range, using the specified scheduler to send out observer messages. 1 - res = Rx.Observable.range(0, 10) 2 - res = Rx.Observable.range(0, 10, rx.Scheduler.timeout) Keyword arguments: start -- The value of the first integer in the sequence. count -- The number of sequential integers to generate. scheduler -- [Optional] Scheduler to run the generator loop on. If not specified, defaults to Scheduler.current_thread. Returns an observable sequence that contains a range of sequential integral numbers. """ scheduler = scheduler or current_thread_scheduler sd = MultipleAssignmentDisposable() end = start + count def subscribe(observer): def action(scheduler, n): if n < end: observer.on_next(n) sd.disposable = scheduler.schedule(action, n + 1) else: observer.on_completed() sd.disposable = scheduler.schedule(action, start) return sd return AnonymousObservable(subscribe)
def subscribe(observer): sd = MultipleAssignmentDisposable() iterator = iter(iterable) def action(scheduler, state=None): try: with lock: item = next(iterator) except StopIteration: observer.on_completed() else: observer.on_next(item) sd.disposable = scheduler.schedule(action) sd.disposable = scheduler.schedule(action) return sd
def subscribe(observer): 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
def subscribe(observer, scheduler=None): nonlocal duetime if isinstance(duetime, int): 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
def schedule_relative(self, timedelta: Union[int, float], action: Callable[[Scheduler, Any], None], state=None): disposable = [MultipleAssignmentDisposable()] def _(): def __(): def func(): action(self, state) future = self.executor.submit(func) disposable[0] = Disposable.create(lambda: future.cancel()) self.loop.call_later(timedelta, __) future = self.loop.call_soon_threadsafe(_) # super().schedule_relative(timedelta, __) return CompositeDisposable(disposable, Disposable.create(lambda: future.cancel()))
def from_iterable(cls, iterable, scheduler=None): """Converts an array to an observable sequence, using an optional scheduler to enumerate the array. 1 - res = rx.Observable.from_iterable([1,2,3]) 2 - res = rx.Observable.from_iterable([1,2,3], rx.Scheduler.timeout) Keyword arguments: :param Observable cls: Observable class :param Scheduler scheduler: [Optional] Scheduler to run the enumeration of the input sequence on. :returns: The observable sequence whose elements are pulled from the given enumerable sequence. :rtype: Observable """ scheduler = scheduler or current_thread_scheduler sd = MultipleAssignmentDisposable() lock = config["concurrency"].RLock() def subscribe(observer): iterator = iter(iterable) def action(scheduler, state=None): try: with lock: item = next(iterator) except StopIteration: observer.on_completed() else: observer.on_next(item) sd.disposable = scheduler.schedule(action) sd.disposable = scheduler.schedule(action) return sd return AnonymousObservable(subscribe)
def unsafe_subscribe(self, observer, scheduler, subscribe_scheduler): state = [self.WaitForLeftOrRight()] inner_left_completed = [0] right_completed = [False] inner_disposable = MultipleAssignmentDisposable() source = self def on_next_left(left_elem): ack = Ack() with self.lock: if isinstance(state[0], self.WaitForLeftOrRight): new_state = self.WaitForRightOrInner(left_ack=ack) elif isinstance(state[0], self.WaitForLeft): state_: source.WaitForLeft = state[0] new_state = self.Active(left_ack=ack, right_elem=state_.right_elem, right_ack=state_.right_ack, upper_ack=Continue()) elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError state[0] = new_state class ChildObserver(Observer): def __init__(self, out: Observer, scheduler): self.out = out self.scheduler = scheduler def on_next(self, inner_left): ack = Ack() has_right_elem = False right_elem = None with source.lock: if isinstance(state[0], source.WaitForRightOrInner): # not much to do state_typed: source.WaitForRightOrInner = state[0] state[0] = source.WaitForRight( inner_left_elem=inner_left, left_ack=state_typed.left_ack, inner_left_ack=ack, left_elem=source.selector_left(left_elem)) elif isinstance(state[0], source.Active): # send zipped item to observer state_typed: source.Active = state[0] has_right_elem = True right_elem = state_typed.right_elem state[0] = source.Active( left_ack=state_typed.left_ack, right_elem=state_typed.right_elem, right_ack=state_typed.right_ack, upper_ack=ack) elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError if has_right_elem: # send triple to observer zipped_elem = source.selector( source.selector_left(left_elem), right_elem, inner_left) upper_ack = observer.on_next(zipped_elem) # race condition with on_completed if isinstance(state[0], source.Active): typed_state: source.Active = state[0] typed_state.upper_ack = upper_ack if isinstance(upper_ack, Stop): with source.lock: state[0] = source.Completed() else: def _(v): if isinstance(v, Stop): with source.lock: state[0] = source.Completed() upper_ack.observe_on(scheduler).subscribe(_) return upper_ack else: return ack def on_error(self, err): with source.lock: state[0] = source.Completed() observer.on_error(err) def on_completed(self): back_pressure_right = False back_pressure_left = False left_ack = None right_ack = None upper_ack = None complete_observer = False with source.lock: if isinstance(state[0], source.Active): # normal complete if right_completed[0]: state[0] = source.Completed() complete_observer = True else: # request new left and new right state_typed: source.Active = state[0] back_pressure_right = True back_pressure_left = True left_ack = state_typed.left_ack right_ack = state_typed.right_ack upper_ack = state_typed.upper_ack state[0] = source.WaitForLeftOrRight() elif isinstance(state[0], source.WaitForRightOrInner): # empty inner observable state_typed: source.WaitForRight = state[0] # count up number of inner completed (without right completed) inner_left_completed[0] += 1 state[0] = source.WaitForLeftOrRight() back_pressure_left = True left_ack = state_typed.left_ack upper_ack = Continue() if complete_observer: observer.on_completed() if back_pressure_left or back_pressure_right: def _(v): if isinstance(v, Stop): with source.lock: state[0] = source.Completed() upper_ack.subscribe(_) if back_pressure_left: # upper_ack should not be Stop if isinstance(upper_ack, Continue): left_ack.on_next(upper_ack) left_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe(left_ack) if back_pressure_right: if isinstance(upper_ack, Continue): right_ack.on_next(upper_ack) right_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe( right_ack) child = self.selector_inner(left_elem) child_observer = ChildObserver(observer, scheduler) disposable = child.subscribe(child_observer, scheduler, CurrentThreadScheduler()) inner_disposable.disposable = disposable return ack def on_next_right(right): ack = Ack() upper_ack = None has_inner_left_elem = False request_left_right = False request_right = False with self.lock: if 0 < inner_left_completed[0]: inner_left_completed[0] -= 1 request_right = True upper_ack = Continue() # new_state = self.WaitForLeftOrRight() new_state = state[0] elif isinstance(state[0], self.WaitForLeftOrRight): new_state = self.WaitForLeft(right_elem=right, right_ack=ack) elif isinstance(state[0], self.WaitForRightOrInner): state_: FlatZipObservable.WaitForRightOrInner = state[0] if inner_left_completed[0] == 0: new_state = self.Active(left_ack=state_.left_ack, right_elem=right, right_ack=ack, upper_ack=Continue()) else: inner_left_completed[0] -= 1 new_state = self.WaitForLeftOrRight() elif isinstance(state[0], self.WaitForRight): state_: FlatZipObservable.WaitForRight = state[0] has_inner_left_elem = True left_elem = state_.left_elem left_ack = state_.left_ack inner_left_elem = state_.inner_left_elem inner_left_ack = state_.inner_left_ack new_state = state[0] elif isinstance(state[0], source.Completed): return stop_ack else: raise NotImplementedError state[0] = new_state if has_inner_left_elem: zipped_elem = self.selector(left_elem, right, inner_left_elem) upper_ack = observer.on_next(zipped_elem) if isinstance(upper_ack, Stop): with self.lock: state[0] = self.Completed return upper_ack request_inner_elem = False with self.lock: if 0 < inner_left_completed[0]: # inner left completed, request new left and right new_state = self.WaitForLeftOrRight() request_left_right = True else: # state is the same, change state to active new_state = self.Active(left_ack=left_ack, right_elem=right, right_ack=ack, upper_ack=upper_ack) request_inner_elem = True state[0] = new_state if request_inner_elem: if isinstance(upper_ack, Continue) or isinstance( upper_ack, Stop): inner_left_ack.on_next(upper_ack) inner_left_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe( inner_left_ack) if request_left_right: if isinstance(upper_ack, Continue): left_ack.on_next(upper_ack) left_ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe(left_ack) if request_left_right or request_right: if isinstance(upper_ack, Continue): ack.on_next(upper_ack) ack.on_completed() else: upper_ack.observe_on(scheduler).subscribe(ack) return ack def on_completed_left(): with self.lock: if isinstance(state[0], self.Completed): return if isinstance(state[0], self.WaitForLeftOrRight) or isinstance( state[0], self.WaitForLeft): new_state = self.Completed() else: new_state = state[0] state[0] = new_state if isinstance(state[0], self.Completed): return observer.on_completed() def on_completed_right(): with self.lock: if isinstance(state[0], self.Completed): return if isinstance(state[0], self.WaitForLeftOrRight) or isinstance(state[0], self.WaitForRightOrInner) \ or isinstance(state[0], self.WaitForRightOrInner): new_state = self.Completed() else: new_state = state[0] right_completed[0] = True state[0] = new_state if isinstance(state[0], self.Completed): return observer.on_completed() class LeftObserver(Observer): def on_next(self, v): return on_next_left(v) def on_error(self, exc): return observer.on_error(exc) def on_completed(self): return on_completed_left() class RightObserver(Observer): def on_next(self, v): return on_next_right(v) def on_error(self, exc): return observer.on_error(exc) def on_completed(self): return on_completed_right() left_observer = LeftObserver() d1 = self.left.unsafe_subscribe(left_observer, scheduler, subscribe_scheduler) right_observer = RightObserver() d2 = self.right.unsafe_subscribe(right_observer, scheduler, subscribe_scheduler) return CompositeDisposable(d1, d2, inner_disposable)
def on_next(notification): log.debug("observable_delay_timespan:subscribe:on_next()") should_run = False with source.lock: if notification.value.kind == 'E': del queue[:] queue.append(notification) exception[0] = notification.value.exception should_run = not running[0] else: queue.append( Timestamp(value=notification.value, timestamp=notification.timestamp + duetime)) should_run = not active[0] active[0] = True if should_run: if exception[0]: log.error("*** Exception: %s", exception[0]) observer.on_error(exception[0]) else: mad = MultipleAssignmentDisposable() cancelable.disposable = mad def action(scheduler, state): if exception[0]: log.error( "observable_delay_timespan:subscribe:on_next:action(), exception: %s", exception[0]) return with source.lock: running[0] = True while True: result = None if len(queue) and queue[ 0].timestamp <= scheduler.now: result = queue.pop(0).value if result: result.accept(observer) if not result: break should_continue = False recurse_duetime = 0 if len(queue): should_continue = True diff = queue[0].timestamp - scheduler.now zero = timedelta(0) if isinstance( diff, timedelta) else 0 recurse_duetime = max(zero, diff) else: active[0] = False ex = exception[0] running[0] = False if ex: observer.on_error(ex) elif should_continue: mad.disposable = scheduler.schedule_relative( recurse_duetime, action) mad.disposable = scheduler.schedule_relative( duetime, action)
def generate(cls, initial_state, condition, iterate, result_selector, scheduler=None): """Generates an observable sequence by running a state-driven loop producing the sequence's elements, using the specified scheduler to send out observer messages. 1 - res = rx.Observable.generate(0, lambda x: x < 10, lambda x: x + 1, lambda x: x) 2 - res = rx.Observable.generate(0, lambda x: x < 10, lambda x: x + 1, lambda x: x, Rx.Scheduler.timeout) Keyword arguments: initial_state -- Initial state. condition -- Condition to terminate generation (upon returning False). iterate -- Iteration step function. result_selector -- Selector function for results produced in the sequence. scheduler -- [Optional] Scheduler on which to run the generator loop. If not provided, defaults to CurrentThreadScheduler. Returns the generated sequence. """ scheduler = scheduler or current_thread_scheduler mad = MultipleAssignmentDisposable() def subscribe(observer): first = [True] state = [initial_state] 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 = result_selector(state[0]) except Exception as exception: 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 AnonymousObservable(subscribe)