Using session_state within socketio client events

Summary

We are trying to use python-socketio to communicate with another server. The event listener functions seem to be executed in other threads and do not share the same session_state.
Is there any workaround for this? I have seen some things about multithreading but don’t know if I can do something like that with socketio.

Steps to reproduce

Code snippet:

import streamlit as st
import socketio

if "some_boolean" not in st.session_state:
    st.session_state.some_boolean = True
if "some_param" not in st.session_state:
    st.session_state.some_param = 50

sio = socketio.Client()
sio.connect("http://localhost:6000")

@sio.on("*")
def catch_all(messageType, value):
    if st.session_state.some_boolean:
       st.session_state.some_param = value

Expected behavior:

It would be great if I could access the session_state like that. But any other way would also be great.

Actual behavior:
I am getting following error:

2023-06-21 18:42:55.033 Thread 'Thread-12 (_handle_eio_message)': missing ScriptRunContext
Exception in thread Thread-12 (_handle_eio_message):
Traceback (most recent call last):
  File "*/streamlit/runtime/state/session_state.py", line 370, in __getitem__
    return self._getitem(widget_id, key)
  File "*/streamlit/runtime/state/session_state.py", line 415, in _getitem
    raise KeyError
KeyError
...
KeyError: 'st.session_state has no key "some_boolean". Did you forget to initialize it?

Debug info

  • Streamlit version: 1.21.0
  • Python version: 3.10
  • Using miniconda environment
  • OS version: Ubuntu 22.04
  • Browser version: Firefox

The error you encountered suggests that the event listener function (catch_all) executed by the socketio library is running in a separate thread, which doesn’t have access to the st.session_state object. This is because st.session_state is specific to the Streamlit framework and is not designed to be shared across different threads or external libraries.

To work around this limitation and achieve communication between your Streamlit app and the socketio server, you can consider the following approach:

  1. Instead of directly modifying st.session_state, create a separate object or data structure to store the shared state between the socketio events and your Streamlit app.

  2. In your Streamlit app, define a Streamlit SessionState class (or use an existing implementation) that can handle shared state across different sessions. Here’s an example implementation:

class SessionState:
    def __init__(self):
        self.some_boolean = True
        self.some_param = 50

session_state = SessionState()
  1. Modify your event listener function (catch_all) to update the shared state object (session_state) instead of modifying st.session_state. For example:
@sio.on("*")
def catch_all(messageType, value):
    if session_state.some_boolean:
       session_state.some_param = value
  1. In your Streamlit app, use the session_state object to access and display the shared state values. For example:
st.write("Some Boolean:", session_state.some_boolean)
st.write("Some Parameter:", session_state.some_param)

By following this approach, you create a separate shared state object that can be accessed and modified by both the event listener function and your Streamlit app. This way, you can achieve communication between the socketio server and your Streamlit app while avoiding conflicts with st.session_state.

Remember to manage synchronization and potential race conditions if multiple threads are modifying the shared state simultaneously. Using appropriate synchronization primitives like locks or thread-safe data structures can help ensure data consistency and avoid conflicts.

Note: The provided example is a basic implementation to demonstrate the concept of shared state. Depending on your specific requirements, you may need to adapt and enhance it to suit your needs.

1 Like

Thank you so much! we have solved it by writing into a json file so far, but I will look into creating a new session state class as well :slight_smile:
You would have to pass the instance to all other functions then right?

We would probably have to rewrite quite a bit of code as the “backend” is not really connected to the streamlit app at all right now, other than the socket. Might not be worth it as we are just doing it for a uni project and the crappy json file solution works for now.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.