Is this a good way of stopping an async loop when a session has ended?

Summary

Iā€™m running long-running streaming requests and other async tasks that I want cancelled when an app session ends.

So I spent quite some time searching the forums, the web in general, and streamlitā€™s source for a way to stop an async loop when an app session has ended.

I came up with this simple approach that I havenā€™t seen before and Iā€™m curious to learn what you think about it.

Steps to reproduce

Code snippet:

import asyncio
from datetime import datetime

import streamlit as st
from streamlit.runtime import get_instance
from streamlit.runtime.scriptrunner import get_script_run_ctx


async def watch(test):
    print("new session")
    runtime = get_instance()
    ctx = get_script_run_ctx()
    while runtime.is_active_session(ctx.session_id):
        test.text(str(datetime.now()))
        await asyncio.sleep(0.1)
    print("session ended")

test = st.empty()

asyncio.run(watch(test))

Notes

  • I can close and re-open tabs or reload them at will. The loop will stop iterating and the script runner will shutdown cleanly. I could also abort any ongoing requests or other streams.
  • Yes, get_instance and get_script_run_ctx is considered using internals, but the surface is very limited
  • Ideally iā€™d like a session end signal to stop the coroutine without having to check for state in a loop.
  • I canā€™t use the ScriptRunners on_event SHUTDOWN signal. If the coroutine loop was infinite, it would keep running and never emit the signal.

I hope this is helpful to someone, or insulting enough to elicit a reply :smirk:

3 Likes

I, for one, think itā€™s a clever solution. The caveat, which you already mentioned, is that this is dependent on internals, and these are definitely not guaranteed to stay the same, so I would recommend pinning a specific version of streamlit.

If that works for your use case, thatā€™s great!

1 Like

Which version was used for this example? Iā€™am having the exact same use case and would like to reproduce with Streamlit 1.22, but as expected get_instance is not used anymore.

At the date of the OP, the latest Streamlit release was 1.20.0. You can try that.

1 Like

thx