Beispiel #1
0
def canceller() -> Tuple[AsyncDisposable, CancellationToken]:
    cts = CancellationTokenSource()

    async def cancel() -> None:
        log.debug("cancller, cancelling!")
        cts.cancel()

    return AsyncDisposable.create(cancel), cts.token
Beispiel #2
0
async def test_async_disposable_disposed_twice_calls_once():
    called = []

    async def action():
        called.append(True)

    disp = AsyncDisposable.create(action)
    await disp.dispose_async()
    await disp.dispose_async()

    assert len(called) == 1
Beispiel #3
0
    async def subscribe_async(
            self, observer: AsyncObserver[TSource]) -> AsyncDisposable:
        """Start streaming."""

        self.check_disposed()
        self._observer = observer

        if not self._wait.done():
            self._wait.set_result(True)

        return AsyncDisposable.create(self.dispose_async)
Beispiel #4
0
async def test_async_disposable_works():
    called = []

    async def action():
        called.append(True)

    disp = AsyncDisposable.create(action)

    async with disp:
        assert not called

    assert called
Beispiel #5
0
async def test_async_disposable_disposed():
    called = []

    async def action():
        called.append(True)

    disp = AsyncDisposable.create(action)
    await disp.dispose_async()
    assert called

    with pytest.raises(ObjectDisposedException):  # type: ignore
        async with disp:
            assert not called

    assert called
Beispiel #6
0
    async def subscribe_async(
            self, observer: AsyncObserver[TSource]) -> AsyncDisposable:
        """Subscribe."""

        log.debug("AsyncMultiStream:subscribe_async()")
        self.check_disposed()

        self._observers.append(observer)

        async def dispose() -> None:
            log.debug("AsyncMultiStream:dispose()")
            if observer in self._observers:
                self._observers.remove(observer)

        return AsyncDisposable.create(dispose)
Beispiel #7
0
    async def get(self):
        ctx = obs_ctx.get()
        if "observer" in ctx:
            observer = ctx["observer"]
            if not self.observers:
                await self.on_start()
            if observer not in self.observers:
                sub = await self.values.subscribe_async(observer)

                async def dispose_async():
                    await sub.dispose_async()
                    del self.observers[observer]
                    if not self.observers:
                        await self.on_stop()

                self.observers[observer] = AsyncDisposable.create(
                    dispose_async)
            ctx["subs"].add(self.observers[observer])

        return self.current_value
Beispiel #8
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)
Beispiel #9
0
    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)