def _slice(source: AsyncObservable[TSource]) -> AsyncObservable[TSource]: nonlocal start if start is not None: if start < 0: source = pipe(source, take_last(abs(start))) else: source = pipe(source, skip(start)) if stop is not None: if stop > 0: start = start or 0 source = pipe(source, take(stop - start)) else: source = pipe(source, skip_last(abs(stop))) if step is not None: if step > 1: mapper: Callable[[Any, int], bool] = lambda _, i: i % step == 0 xs = pipe(source, filteri(mapper)) source = xs elif step < 0: # Reversing streams is not supported raise TypeError("Negative step not supported.") return source
async def main(): xs = rx.from_iterable(range(10)) # Split into odds and evens evens = pipe(xs, rx.filter(lambda x: x % 2 == 0)) odds = pipe(xs, rx.filter(lambda x: x % 2 == 1)) async def mysink(value: int): print(value) await odds.subscribe_async(rx.AsyncAnonymousObserver(mysink)) await evens.subscribe_async(rx.AsyncAnonymousObserver(mysink))
def combine_latest( self, other: AsyncObservable[TOther]) -> AsyncRx[Tuple[TSource, TOther]]: from .combine import combine_latest xs = pipe(self, combine_latest(other)) return AsyncRx.create(xs)
async def test_pipe_complex_pipe(): xs = rx.from_iterable([1, 2, 3]) result = [] def mapper(value: int) -> int: return value * 10 async def predicate(value: int) -> bool: await asyncio.sleep(0.1) return value > 1 def long_running(value: int) -> AsyncObservable[int]: return rx.from_iterable([value]) ys = pipe( xs, rx.filter_async(predicate), rx.map(mapper), rx.flat_map(long_running), rx.to_async_iterable, ) async for value in ys: result.append(value) assert result == [20, 30]
def test_seq_take(xs: List[int], x: int): ys = seq.of_iterable(xs) try: zs = pipe(ys, seq.take(x)) assert list(zs) == xs[:x] except ValueError: assert x > len(xs)
def take(self, count: int) -> Seq[TSource]: """Returns the first N elements of the sequence. Args: count: The number of items to take. """ return Seq(pipe(self, take(count)))
async def test_map_mapper_throws(): error = Exception("ex") exception = None xs = rx.from_iterable([1]) async def athrow(ex: Exception): nonlocal exception exception = ex def mapper(x: int): raise error ys = pipe(xs, rx.map(mapper)) obv = rx.AsyncAwaitableObserver(athrow=athrow) await ys.subscribe_async(obv) try: await obv except Exception as ex: assert exception == ex else: assert False
async def test_merge_streams(): xs: AsyncTestSubject[AsyncObservable[int]] = AsyncTestSubject() s1: AsyncTestSubject[int] = AsyncTestSubject() s2: AsyncTestSubject[int] = AsyncTestSubject() ys = pipe(xs, rx.merge_inner()) obv = AsyncTestObserver() await ys.subscribe_async(obv) await xs.asend(s1) await xs.asend(s2) await s1.asend_at(1, 10) await s1.asend_at(2, 20) await s1.asend_at(4, 30) await s1.aclose_at(6) await s2.asend_at(0, 40) await s2.asend_at(3, 50) await s2.asend_at(5, 60) await s2.aclose_at(6) await xs.aclose() await obv assert obv.values == [ (0, OnNext(40)), (1, OnNext(10)), (2, OnNext(20)), (3, OnNext(50)), (4, OnNext(30)), (5, OnNext(60)), (6, OnCompleted), ]
async def test_merge_streams_concat(): s1: AsyncTestSubject[int] = AsyncTestSubject() s2 = rx.from_iterable([1, 2, 3]) xs = rx.from_iterable([s1, s2]) ys = pipe(xs, rx.merge_inner(1)) obv = AsyncTestObserver() await ys.subscribe_async(obv) await s1.asend_at(1, 10) await s1.asend_at(2, 20) await s1.asend_at(4, 30) await s1.aclose_at(6) await obv assert obv.values == [ (1, OnNext(10)), (2, OnNext(20)), (4, OnNext(30)), (6, OnNext(1)), (6, OnNext(2)), (6, OnNext(3)), (6, OnCompleted), ]
def flat_map_async( self, selector: Callable[[TSource], Awaitable[AsyncObservable[TResult]]] ) -> AsyncRx[TResult]: from .transform import flat_map_async return AsyncRx.create(pipe(self, flat_map_async(selector)))
def concat(sources: Iterable[FrozenList[TSource]]) -> FrozenList[TSource]: """Concatenate sequence of FrozenList's""" def reducer(t: FrozenList[TSource], s: FrozenList[TSource]) -> FrozenList[TSource]: return t.append(s) return pipe(sources, seq.fold(reducer, empty))
def skip(self, count: int) -> Seq[TSource]: """Returns a sequence that skips N elements of the underlying sequence and then yields the remaining elements of the sequence. Args: count: The number of items to skip. """ return Seq(pipe(self, skip(count)))
def test_seq_choose_option(): xs: Iterable[Optional[int]] = seq.of(None, 42) chooser: seq.Projection[Optional[int], int] = seq.choose(option.of_optional) ys = pipe(xs, chooser) assert list(ys) == [42]
def test_list_filter(xs: List[int], limit: int): expected = filter(lambda x: x < limit, xs) ys: FrozenList[int] = frozenlist.of_seq(xs) predicate: Callable[[int], bool] = lambda x: x < limit result = pipe(ys, frozenlist.filter(predicate)) assert list(result) == list(expected)
def __str__(self) -> str: def to_str(item: Tuple[Key, Value]) -> str: key, value = item if isinstance(key, str): return f'("{key}", {value})' return f"({key}, {value})" items = pipe(self.to_seq(), seq.map(to_str)) return f"map [{'; '.join(items)}]"
def merge_seq( sources: Iterable[AsyncObservable[TSource]] ) -> AsyncObservable[TSource]: from .create import of_seq return pipe( of_seq(sources), merge_inner(), )
async def test_distinct_until_changed_different(): xs = rx.from_iterable([1, 2, 3]) obv: AsyncTestObserver[int] = AsyncTestObserver() ys = pipe(xs, rx.distinct_until_changed) await rx.run(ys, obv) assert obv.values == [(0, OnNext(1)), (0, OnNext(2)), (0, OnNext(3)), (0, OnCompleted)]
def test_list_unfold(x: int): def unfolder(state: int) -> Option[Tuple[int, int]]: if state < x: return Some((state, state + 1)) return Nothing result = pipe(0, seq.unfold(unfolder)) assert list(result) == list(range(x))
def concat_seq( sources: Iterable[AsyncObservable[TSource]] ) -> AsyncObservable[TSource]: """Returns an observable sequence that contains the elements of each given sequences, in sequential order.""" return pipe( of_seq(sources), merge_inner(1), )
def test_list_fold(xs: List[int]): def folder(x: int, y: int) -> int: return x + y expected: int = functools.reduce(folder, xs, 0) ys: FrozenList[int] = frozenlist.of_seq(xs) result = pipe(ys, frozenlist.fold(folder, 0)) assert result == expected
def starmap(self, mapper: Callable[..., TResult]) -> AsyncObservable[TResult]: """Map and spread the arguments to the mapper. Returns: An observable sequence whose elements are the result of invoking the mapper function on each element of the source. """ return AsyncRx(pipe(self, starmap(mapper)))
async def test_take_zero() -> None: xs = rx.from_iterable([1, 2, 3, 4, 5]) ys = pipe(xs, rx.take(0)) obv: AsyncTestObserver[int] = AsyncTestObserver() with pytest.raises(CancelledError): await rx.run(ys, obv) assert obv.values == [(0, OnCompleted)]
async def test_take_empty() -> None: xs: AsyncObservable[int] = rx.empty() ys = pipe(xs, rx.take(42)) obv: AsyncTestObserver[int] = AsyncTestObserver() with pytest.raises(CancelledError): await rx.run(ys, obv) assert obv.values == [(0, OnCompleted)]
async def test_flat_map_monad(): m = rx.single(42) def mapper(x: int) -> AsyncObservable[int]: return rx.single(x * 10) a = await rx.run(pipe(m, rx.flat_map(mapper))) b = await rx.run(rx.single(420)) assert a == b
async def test_take_normal() -> None: xs = rx.from_iterable([1, 2, 3, 4, 5]) ys = pipe(xs, rx.take(2)) obv: AsyncTestObserver[int] = AsyncTestObserver() result = await rx.run(ys, obv) assert result == 2 assert obv.values == [(0, OnNext(1)), (0, OnNext(2)), (0, OnCompleted)]
def test_map_remove(xs: Dict[str, int]): items: ItemsView[str, int] = xs.items() m = Map.of_seq(items) keys = xs.keys() count = len(m) for key in keys: m = pipe(m, map.remove(key)) count -= 1 assert len(m) == count == 0
def starfilter(self: AsyncObservable[Iterable[Any]], predicate: Callable[..., bool]) -> AsyncRx[Iterable[Any]]: """Filter and spread the arguments to the predicate. Filters the elements of an observable sequence based on a predicate. Returns: An observable sequence that contains elements from the input sequence that satisfy the condition. """ xs = pipe(self, starfilter(predicate)) return AsyncRx.create(xs)
async def test_flat_map_monad_law_associativity(): # (m >>= f) >>= g is just like doing m >>= (\x -> f x >>= g) m = rx.single(42) def f(x: int) -> AsyncObservable[int]: return rx.single(x + 1000) def g(y: int) -> AsyncObservable[int]: return rx.single(y * 333) def h(x: int) -> AsyncObservable[int]: return pipe(f(x), rx.flat_map(g)) zs = pipe(m, rx.flat_map(f)) a = await rx.run(pipe(zs, rx.flat_map(g))) b = await rx.run(pipe(m, rx.flat_map(h))) assert a == b
async def test_withlatestfrom_never_empty(): xs: AsyncObservable[int] = rx.empty() ys: AsyncObservable[int] = rx.never() zs = pipe(xs, rx.with_latest_from(ys)) obv: AsyncTestObserver[Tuple[int, int]] = AsyncTestObserver() with pytest.raises(CancelledError): await rx.run(zs, obv) assert obv.values == [(0, OnCompleted)]
def to_seq(s: MapTree[Key, Value]) -> Iterable[Tuple[Key, Value]]: it = mk_iterator(s) def folder(it: Iterator[Tuple[Key, Value]]): try: current = next(it) except StopIteration: return Nothing else: return Some((current, it)) return pipe(it, seq.unfold(folder))