コード例 #1
0
        async def subscribe_async(
                aobv: AsyncObserver[TSource]) -> AsyncDisposable:
            safe_obv, auto_detach = auto_detach_observer(aobv)
            infinite: Iterable[int] = seq.infinite

            async def worker(
                inbox: MailboxProcessor[Tuple[Notification[TSource], int]]
            ) -> None:
                @tailrec_async
                async def message_loop(
                        current_index: int) -> TailCallResult[NoReturn]:
                    n, index = await inbox.receive()

                    with match(n) as case:
                        log.debug("debounce: %s, %d, %d", n, index,
                                  current_index)
                        for x in case(OnNext[TSource]):
                            if index == current_index:
                                await safe_obv.asend(x)
                                current_index = index
                            elif index > current_index:
                                current_index = index

                        for err in case(OnError[TSource]):
                            await safe_obv.athrow(err)

                        while case(OnCompleted):
                            await safe_obv.aclose()

                    return TailCall(current_index)

                await message_loop(-1)

            agent = MailboxProcessor.start(worker)

            indexer = iter(infinite)

            async def obv(n: Notification[TSource]) -> None:
                index = next(indexer)
                agent.post((n, index))

                async def worker() -> None:
                    log.debug("debounce, sleeping: %s", seconds)
                    await asyncio.sleep(seconds)
                    agent.post((n, index))

                aiotools.start(worker())

            obv_: AsyncObserver[TSource] = AsyncNotificationObserver(obv)
            dispose = await pipe(obv_, source.subscribe_async, auto_detach)

            async def cancel() -> None:
                await dispose.dispose_async()
                agent.post((OnCompleted, 0))

            return AsyncDisposable.create(cancel)
コード例 #2
0
        async def subscribe_async(
                aobv: AsyncObserver[TSource]) -> AsyncDisposable:
            async def worker(
                inbox: MailboxProcessor[Tuple[Notification[TSource], datetime]]
            ) -> None:
                @tailrec_async
                async def loop() -> TailCallResult[None]:
                    if token.is_cancellation_requested:
                        return

                    ns, due_time = await inbox.receive()

                    diff = due_time - datetime.utcnow()
                    seconds = diff.total_seconds()
                    if seconds > 0:
                        await asyncio.sleep(seconds)

                    async def matcher() -> None:
                        with match(ns) as case:
                            for x in case(OnNext[TSource]):
                                await aobv.asend(x)
                                return
                            for err in case(OnError[TSource]):
                                await aobv.athrow(err)
                                return
                            for x in case(OnCompleted):
                                await aobv.aclose()
                                return

                    await matcher()
                    return TailCall()

                asyncio.ensure_future(loop())

            agent = MailboxProcessor.start(worker, token)

            async def fn(ns: Notification[TSource]) -> None:
                due_time = datetime.utcnow() + timedelta(seconds=seconds)
                agent.post((ns, due_time))

            obv: AsyncNotificationObserver[
                TSource] = AsyncNotificationObserver(fn)
            subscription = await source.subscribe_async(obv)

            async def cancel() -> None:
                log.debug("delay:cancel()")
                cts.cancel()
                await subscription.dispose_async()

            return AsyncDisposable.create(cancel)
コード例 #3
0
async def test_mailbox(xs: List[int]) -> None:
    result: List[int] = []

    async def process(inbox: MailboxProcessor[int]):
        """the message processing function."""
        async def message_loop() -> None:
            msg: int = await inbox.receive()
            result.append(msg)

            return await message_loop()

        return await message_loop()  # start the loop

    agent = MailboxProcessor.start(process)
    for x in xs:
        agent.post(x)
    await asyncio.sleep(0)

    assert result == xs
コード例 #4
0
    async def subscribe_async(aobv: AsyncObserver[TSource]) -> AsyncDisposable:
        safe_obv, auto_detach = auto_detach_observer(aobv)

        async def worker(
                inbox: MailboxProcessor[Notification[TSource]]) -> None:
            @tailrec_async
            async def message_loop(
                    latest: Notification[TSource]) -> TailCallResult[NoReturn]:
                n = await inbox.receive()

                async def get_latest() -> Notification[TSource]:
                    with match(n) as case:
                        for x in case(OnNext[TSource]):
                            if n == latest:
                                break
                            try:
                                await safe_obv.asend(x)
                            except Exception as ex:
                                await safe_obv.athrow(ex)
                            break
                        for err in case(OnError[TSource]):
                            await safe_obv.athrow(err)
                            break
                        while case(OnCompleted):
                            await safe_obv.aclose()
                            break

                    return n

                latest = await get_latest()
                return TailCall(latest)

            await message_loop(
                OnCompleted
            )  # Use as sentinel value as it will not match any OnNext value

        agent = MailboxProcessor.start(worker)

        async def notification(n: Notification[TSource]) -> None:
            agent.post(n)

        obv: AsyncObserver[TSource] = AsyncNotificationObserver(notification)
        return await pipe(obv, source.subscribe_async, auto_detach)
コード例 #5
0
async def test_mailbox_post_and_async_reply(x: int):
    async def process(inbox: MailboxProcessor[Tuple[int,
                                                    AsyncReplyChannel[str]]]):
        """the message processing function."""
        async def message_loop() -> None:
            msg, rc = await inbox.receive()
            rc.reply(f"Got {msg}")

            return await message_loop()

        # start the loop
        return await message_loop()

    agent: MailboxProcessor[Tuple[
        int, AsyncReplyChannel[str]]] = MailboxProcessor.start(process)
    build_message: Callable[[AsyncReplyChannel[str]],
                            Tuple[int,
                                  AsyncReplyChannel[str]]] = lambda r: (x, r)
    reply = await agent.post_and_async_reply(build_message)

    assert reply == f"Got {x}"
コード例 #6
0
    async def subscribe_async(aobv: AsyncObserver[TSource]) -> AsyncDisposable:
        safe_obv, auto_detach = auto_detach_observer(aobv)

        def obv(mb: MailboxProcessor[Msg[TSource]], id: int):
            async def asend(value: TSource) -> None:
                await safe_obv.asend(value)

            async def athrow(error: Exception) -> None:
                await safe_obv.athrow(error)

            async def aclose() -> None:
                pipe(Key(id), InnerCompletedMsg, mb.post)

            return AsyncAnonymousObserver(asend, athrow, aclose)

        async def worker(inbox: MailboxProcessor[Msg[TSource]]) -> None:
            @tailrec_async
            async def message_loop(current: Option[AsyncDisposable],
                                   is_stopped: bool,
                                   current_id: int) -> TailCallResult[None]:
                cmd = await inbox.receive()

                with match(cmd) as case:
                    for xs in case(InnerObservableMsg[TSource]):
                        next_id = current_id + 1
                        for disp in current.to_list():
                            await disp.dispose_async()
                        inner = await xs.subscribe_async(obv(inbox, next_id))
                        current, current_id = Some(inner), next_id
                        break
                    for idx in case(InnerCompletedMsg[Key]):
                        if is_stopped and idx == current_id:
                            await safe_obv.aclose()
                            current, is_stopped = Nothing, True
                        break
                    while case(CompletedMsg):
                        if current.is_none():
                            await safe_obv.aclose()
                        break
                    while case(DisposeMsg):
                        if current.is_some():
                            await current.value.dispose_async()
                        current, is_stopped = Nothing, True
                        break

                return TailCall(current, is_stopped, current_id)

            await message_loop(Nothing, False, 0)

        inner_agent = MailboxProcessor.start(worker)

        async def asend(xs: AsyncObservable[TSource]) -> None:
            pipe(
                InnerObservableMsg(xs),
                inner_agent.post,
            )

        async def athrow(error: Exception) -> None:
            await safe_obv.athrow(error)

        async def aclose() -> None:
            inner_agent.post(CompletedMsg)

        _obv = AsyncAnonymousObserver(asend, athrow, aclose)
        dispose = await pipe(
            _obv,
            AsyncObserver,
            source.subscribe_async,
            auto_detach,
        )

        async def cancel() -> None:
            await dispose.dispose_async()
            inner_agent.post(DisposeMsg)

        return AsyncDisposable.create(cancel)
コード例 #7
0
async def main() -> None:
    root = Tk()
    root.title("aioreactive")

    mousemoves: AsyncSubject[Tuple[int, int]] = AsyncSubject()

    frame = Frame(root, width=800, height=600, bg="white")

    async def worker(mb: MailboxProcessor["Event[Misc]"]) -> None:
        while True:
            event = await mb.receive()
            await mousemoves.asend((event.x, event.y))

    agent = MailboxProcessor.start(worker)
    frame.bind("<Motion>", agent.post)

    text = "TIME FLIES LIKE AN ARROW"
    labels = [Label(frame, text=c, bg="white") for c in text]

    def handle_label(label: Label,
                     i: int) -> AsyncObservable[Tuple[Label, int, int]]:
        label.config(dict(borderwidth=0, padx=0, pady=0))

        def mapper(x: int, y: int) -> Tuple[Label, int, int]:
            """Map mouse-move pos to label and new pos for label."""
            return label, x + i * 12 + 15, y

        return pipe(
            mousemoves,  # stream of mouse-moves
            rx.delay(i / 10.0),  # delay each mouse-move based on index of char
            rx.starmap(
                mapper),  # place label based on mouse pos and index of char
        )

    stream = pipe(
        labels,  # list of labels
        rx.from_iterable,  # stream of labels
        rx.flat_mapi(
            handle_label),  # swap stream of labels with stream of labels + pos
    )

    async def asend(value: Tuple[Label, int, int]) -> None:
        """Perform side effect."""
        label, x, y = value
        label.place(x=x, y=y)

    async def athrow(ex: Exception):
        print("Exception: ", ex)

    obv = AsyncAnonymousObserver(asend, athrow)

    subscription: AsyncDisposable

    async def start():
        nonlocal subscription
        print("Subscribing stream")
        subscription = await stream.subscribe_async(obv)

    async def stop():
        nonlocal subscription
        print("Disposing stream")
        await subscription.dispose_async()

    def handle_focus_in(event: "Event[Misc]"):
        asyncio.ensure_future(start())

    def handle_focus_out(event: "Event[Misc]"):
        asyncio.ensure_future(stop())

    root.bind("<FocusIn>", handle_focus_in)
    root.bind("<FocusOut>", handle_focus_out)

    frame.pack()

    running = True

    def signal_handler(signal: int, frame: FrameType) -> None:
        nonlocal running
        running = False
        sys.stderr.write("Exiting...\n")
        root.destroy()
        root.quit()

    signal.signal(signal.SIGINT, signal_handler)

    # A simple combined event loop
    while running:
        await asyncio.sleep(0.001)
        try:
            root.update()  # type: ignore
        except Exception:
            pass
コード例 #8
0
ファイル: combine.py プロジェクト: seungyongshim/aioreactive
    def _(
        source: AsyncObservable[AsyncObservable[TSource]]
    ) -> AsyncObservable[TSource]:
        async def subscribe_async(
                aobv: AsyncObserver[TSource]) -> AsyncDisposable:
            safe_obv, auto_detach = auto_detach_observer(aobv)

            initial_model = Model(
                subscriptions=map.empty,
                queue=frozenlist.empty,
                is_stopped=False,
                key=Key(0),
            )

            async def worker(inbox: MailboxProcessor[Msg[TSource]]) -> None:
                def obv(key: Key) -> AsyncObserver[TSource]:
                    async def asend(value: TSource) -> None:
                        await safe_obv.asend(value)

                    async def athrow(error: Exception) -> None:
                        await safe_obv.athrow(error)

                    async def aclose() -> None:
                        inbox.post(InnerCompletedMsg(key))

                    return AsyncAnonymousObserver(asend, athrow, aclose)

                async def update(msg: Msg[TSource],
                                 model: Model[TSource]) -> Model[TSource]:
                    # log.debug("update: %s, model: %s", msg, model)
                    with match(msg) as case:
                        for xs in case(InnerObservableMsg[TSource]):
                            if max_concurrent == 0 or len(
                                    model.subscriptions) < max_concurrent:
                                inner = await xs.subscribe_async(obv(model.key)
                                                                 )
                                return model.replace(
                                    subscriptions=model.subscriptions.add(
                                        model.key, inner),
                                    key=Key(model.key + 1),
                                )
                            lst = FrozenList.singleton(xs)
                            return model.replace(queue=model.queue.append(lst))
                        for key in case(InnerCompletedMsg[Key]):
                            subscriptions = model.subscriptions.remove(key)
                            if len(model.queue):
                                xs = model.queue[0]
                                inner = await xs.subscribe_async(obv(model.key)
                                                                 )

                                return model.replace(
                                    subscriptions=subscriptions.add(
                                        model.key, inner),
                                    key=model.key + 1,
                                    queue=model.queue.tail(),
                                )
                            elif len(subscriptions):
                                return model.replace(
                                    subscriptions=subscriptions)
                            else:
                                if model.is_stopped:
                                    await safe_obv.aclose()
                                return model.replace(subscriptions=map.empty)
                        while case(CompletedMsg):
                            if not model.subscriptions:
                                log.debug("merge_inner: closing!")
                                await safe_obv.aclose()

                            return model.replace(is_stopped=True)

                        while case.default():
                            for dispose in model.subscriptions.values():
                                await dispose.dispose_async()

                        return initial_model.replace(is_stopped=True)

                async def message_loop(model: Model[TSource]) -> None:
                    while True:
                        msg = await inbox.receive()
                        model = await update(msg, model)

                        if model.is_stopped and not model.subscriptions:
                            break

                await message_loop(initial_model)

            agent = MailboxProcessor.start(worker)

            async def asend(xs: AsyncObservable[TSource]) -> None:
                log.debug("merge_inner:asend(%s)", xs)
                agent.post(InnerObservableMsg(inner_observable=xs))

            async def athrow(error: Exception) -> None:
                await safe_obv.athrow(error)
                agent.post(DisposeMsg)

            async def aclose() -> None:
                agent.post(CompletedMsg)

            obv = AsyncAnonymousObserver(asend, athrow, aclose)
            dispose = await auto_detach(source.subscribe_async(obv))

            async def cancel() -> None:
                await dispose.dispose_async()
                agent.post(DisposeMsg)

            return AsyncDisposable.create(cancel)
コード例 #9
0
ファイル: combine.py プロジェクト: seungyongshim/aioreactive
    def _with_latest_from(
        source: AsyncObservable[TSource]
    ) -> AsyncObservable[Tuple[TSource, TOther]]:
        async def subscribe_async(
                aobv: AsyncObserver[Tuple[TSource,
                                          TOther]]) -> AsyncDisposable:
            safe_obv, auto_detach = auto_detach_observer(aobv)

            async def worker(inbox: MailboxProcessor[Msg[TSource]]) -> None:
                @tailrec_async
                async def message_loop(
                        latest: Option[TOther]) -> TailCallResult[NoReturn]:
                    cn = await inbox.receive()

                    async def get_value(n: Notification[Any]) -> Option[Any]:
                        with match(n) as case:
                            for value in case(OnNext[TSource]):
                                return Some(value)

                            for err in case(OnError[TSource]):
                                await safe_obv.athrow(err)

                            while case.default():
                                await safe_obv.aclose()
                        return Nothing

                    source_value = Nothing
                    if isinstance(cn, SourceMsg):
                        cn = cast(SourceMsg[TSource], cn)
                        source_value = await get_value(cn.value)
                    else:
                        cn = cast(OtherMsg[TOther], cn)
                        latest = await get_value(cn.value)

                    def binder(s: TSource) -> Option[Tuple[TSource, TOther]]:
                        def mapper(o: TOther) -> Tuple[TSource, TOther]:
                            return (s, o)

                        return latest.map(mapper)

                    combined = source_value.bind(binder)
                    for x in combined.to_list():
                        await safe_obv.asend(x)

                    return TailCall(latest)

                await message_loop(Nothing)

            agent = MailboxProcessor.start(worker)

            async def obv_fn1(n: Notification[TSource]) -> None:
                pipe(SourceMsg(n), agent.post)

            async def obv_fn2(n: Notification[TOther]) -> None:
                pipe(OtherMsg(n), agent.post)

            obv1: AsyncObserver[TSource] = AsyncNotificationObserver(obv_fn1)
            obv2: AsyncObserver[TOther] = AsyncNotificationObserver(obv_fn2)
            dispose1 = await pipe(obv1, source.subscribe_async, auto_detach)
            dispose2 = await pipe(obv2, other.subscribe_async, auto_detach)
            return AsyncDisposable.composite(dispose1, dispose2)