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 :smirking_face:

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!

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.

thx