Ejemplo n.º 1
0
        async def subscribe_async(
                aobv: AsyncObserver[TSource]) -> AsyncDisposable:
            disposable = AsyncDisposable.empty()

            async def action(source: AsyncObservable[TSource]) -> None:
                nonlocal disposable

                async def asend(value: TSource) -> None:
                    await aobv.asend(value)

                async def athrow(error: Exception) -> None:
                    next_source = handler(error)
                    await action(next_source)

                async def aclose() -> None:
                    await aobv.aclose()

                _obv = AsyncAnonymousObserver(asend, athrow, aclose)

                await disposable.dispose_async()
                subscription = await source.subscribe_async(_obv)
                disposable = subscription

            await action(source)

            return AsyncDisposable.create(disposable.dispose_async)
Ejemplo n.º 2
0
async def autorun(func):
    async def run_func():
        # TODO: write unit test for conditional observer. Something like:
        # if pair.y > 10:
        #   print(pair.x)
        # and then change x when y is less than 10.
        prev_subs = ctx["subs"]
        ctx["subs"] = set()

        parent_ctx = obs_ctx.get()
        obs_ctx.set(ctx)
        await func()
        obs_ctx.set(parent_ctx)

        for sub in prev_subs - ctx["subs"]:
            await sub.dispose_async()

    ctx = {
        "observer": await BufferedObserver(dropargs(run_func)),
        "subs": set()
    }

    async def dispose_async():
        nonlocal ctx, func
        for sub in ctx["subs"]:
            await sub.dispose_async()
        await ctx["observer"].dispose_async()
        del ctx
        del func

    await run_func()

    sub = AsyncDisposable.create(dispose_async)
    return sub
Ejemplo n.º 3
0
    async def subscribe_async(observer: AsyncObserver[TSource]) -> AsyncDisposable:
        task: Optional[Future[None]] = None

        async def cancel():
            if task is not None:
                task.cancel()

        sub = AsyncDisposable.create(cancel)

        async def worker() -> None:
            async for value in iterable:
                try:
                    await observer.asend(value)
                except Exception as ex:
                    await observer.athrow(ex)
                    return

            await observer.aclose()

        try:
            task = asyncio.ensure_future(worker())
        except Exception as ex:
            log.debug("FromIterable:worker(), Exception: %s" % ex)
            await observer.athrow(ex)
        return sub
Ejemplo n.º 4
0
def canceller() -> Tuple[AsyncDisposable, CancellationToken]:
    cts = CancellationTokenSource()

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

    return AsyncDisposable.create(cancel), cts.token
Ejemplo n.º 5
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
Ejemplo n.º 6
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)
Ejemplo n.º 7
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
Ejemplo n.º 8
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
Ejemplo n.º 9
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)
Ejemplo n.º 10
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
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
 async def subscribe_async(_: AsyncObserver[Any]) -> AsyncDisposable:
     return AsyncDisposable.empty()
Ejemplo n.º 13
0
 async def subscribe_async(aobv: AsyncObserver[Any]) -> AsyncDisposable:
     await aobv.aclose()
     return AsyncDisposable.empty()
Ejemplo n.º 14
0
    async def subscribe_async(aobv: AsyncObserver[TSource]) -> AsyncDisposable:
        safe_obv = safe_observer(aobv, AsyncDisposable.empty())

        await safe_obv.asend(value)
        await safe_obv.aclose()
        return AsyncDisposable.empty()
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
    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)