Shiny for Python: using async for

I have a network client that connects to a streaming server and returns the messages it receives via the asynchronous iterator protocol. The messages are then added to a "storage" object as follows:

storage = MessageStorage()

async with NetworkClient(uri) as client:
    await client.setup(...)
    async for message in client:
        storage.add(message)

In Shiny for Python, I'd like to connect to the server and display the storage as soon as it changes. How do I do that? :slight_smile:

Wrapping the code above in an async function definition as e.g.

@reactive.effect
async def start_streaming():
    async with NetworkClient(uri) as client:
        ...

blocks the application, and the UI never renders.

Calling the co-routine with asyncio.create_task does not block, but the for loop body is called way too often.

@reactive.effect
async def _():
    asyncio.create_task(start_streaming())

AFAIK, the @extended_task iterator won't help here either, because it is designed to run a long-running task which returns, whereas my loop doesn't return. I tried yielding, but got this error:

ExtendedTask can only be used with async functions".

Maybe I wasn't calling it correctly, though.

For receiving data in the background, this worked: creating the asyncio task from within a reactive.effect-wrapped function.

@reactive.effect
async def start_streaming():
    async def stream():
        async with NetworkClient(uri) as client:
            # ...

    stream_task = asyncio.create_task(stream())

The interface still isn't rendered correctly though.

Tracking the state of storage using reactive.value didn't work, as expected. Creating a "counter" reactive.value() object and updating it each time a new message was received did work, but only when the interface is implicitly updated by e.g. this timer:

@render.text
def time():
    reactive.invalidate_later(5)
    return datetime.now().strftime('%H:%M:%S')

This would redraw the plot every 5 seconds, even though the data was available, and counter changed.

1 Like