90 lines
2.6 KiB
Python
90 lines
2.6 KiB
Python
from typing import Any, Callable, Optional, TypeVar, cast
|
|
|
|
from reactivex import Observable, abc
|
|
from reactivex.disposable import MultipleAssignmentDisposable
|
|
from reactivex.scheduler import TimeoutScheduler
|
|
from reactivex.typing import Mapper, Predicate, RelativeTime
|
|
|
|
_TState = TypeVar("_TState")
|
|
|
|
|
|
def generate_with_relative_time_(
|
|
initial_state: _TState,
|
|
condition: Predicate[_TState],
|
|
iterate: Mapper[_TState, _TState],
|
|
time_mapper: Callable[[_TState], RelativeTime],
|
|
) -> Observable[_TState]:
|
|
"""Generates an observable sequence by iterating a state from an
|
|
initial state until the condition fails.
|
|
|
|
Example:
|
|
res = source.generate_with_relative_time(
|
|
0, lambda x: True, lambda x: x + 1, lambda x: 0.5
|
|
)
|
|
|
|
Args:
|
|
initial_state: Initial state.
|
|
condition: Condition to terminate generation (upon returning
|
|
false).
|
|
iterate: Iteration step function.
|
|
time_mapper: Time mapper function to control the speed of
|
|
values being produced each iteration, returning relative
|
|
times, i.e. either floats denoting seconds or instances of
|
|
timedelta.
|
|
|
|
Returns:
|
|
The generated sequence.
|
|
"""
|
|
|
|
def subscribe(
|
|
observer: abc.ObserverBase[_TState],
|
|
scheduler: Optional[abc.SchedulerBase] = None,
|
|
) -> abc.DisposableBase:
|
|
scheduler = scheduler or TimeoutScheduler.singleton()
|
|
mad = MultipleAssignmentDisposable()
|
|
state = initial_state
|
|
has_result = False
|
|
result: _TState = cast(_TState, None)
|
|
first = True
|
|
time: Optional[RelativeTime] = None
|
|
|
|
def action(scheduler: abc.SchedulerBase, _: Any) -> None:
|
|
nonlocal state
|
|
nonlocal has_result
|
|
nonlocal result
|
|
nonlocal first
|
|
nonlocal time
|
|
|
|
if has_result:
|
|
observer.on_next(result)
|
|
|
|
try:
|
|
if first:
|
|
first = False
|
|
else:
|
|
state = iterate(state)
|
|
|
|
has_result = condition(state)
|
|
|
|
if has_result:
|
|
result = state
|
|
time = time_mapper(state)
|
|
|
|
except Exception as e: # pylint: disable=broad-except
|
|
observer.on_error(e)
|
|
return
|
|
|
|
if has_result:
|
|
assert time
|
|
mad.disposable = scheduler.schedule_relative(time, action)
|
|
else:
|
|
observer.on_completed()
|
|
|
|
mad.disposable = scheduler.schedule_relative(0, action)
|
|
return mad
|
|
|
|
return Observable(subscribe)
|
|
|
|
|
|
__all__ = ["generate_with_relative_time_"]
|