"""Hack to add per-session state to Streamlit. | |
Usage | |
----- | |
>>> import SessionState | |
>>> | |
>>> session_state = SessionState.get(user_name='', favorite_color='black') | |
>>> session_state.user_name | |
'' | |
>>> session_state.user_name = 'Mary' | |
>>> session_state.favorite_color | |
'black' | |
Since you set user_name above, next time your script runs this will be the | |
result: | |
>>> session_state = get(user_name='', favorite_color='black') | |
>>> session_state.user_name | |
'Mary' | |
""" | |
try: | |
import streamlit.ReportThread as ReportThread | |
from streamlit.server.Server import Server | |
except Exception: | |
# Streamlit >= 0.65.0 | |
import streamlit.report_thread as ReportThread | |
from streamlit.server.server import Server | |
class SessionState(object): | |
def __init__(self, **kwargs): | |
"""A new SessionState object. | |
Parameters | |
---------- | |
**kwargs : any | |
Default values for the session state. | |
Example | |
------- | |
>>> session_state = SessionState(user_name='', favorite_color='black') | |
>>> session_state.user_name = 'Mary' | |
'' | |
>>> session_state.favorite_color | |
'black' | |
""" | |
for key, val in kwargs.items(): | |
setattr(self, key, val) | |
def get(**kwargs): | |
"""Gets a SessionState object for the current session. | |
Creates a new object if necessary. | |
Parameters | |
---------- | |
**kwargs : any | |
Default values you want to add to the session state, if we're creating a | |
new one. | |
Example | |
------- | |
>>> session_state = get(user_name='', favorite_color='black') | |
>>> session_state.user_name | |
'' | |
>>> session_state.user_name = 'Mary' | |
>>> session_state.favorite_color | |
'black' | |
Since you set user_name above, next time your script runs this will be the | |
result: | |
>>> session_state = get(user_name='', favorite_color='black') | |
>>> session_state.user_name | |
'Mary' | |
""" | |
# Hack to get the session object from Streamlit. | |
ctx = ReportThread.get_report_ctx() | |
this_session = None | |
current_server = Server.get_current() | |
if hasattr(current_server, '_session_infos'): | |
# Streamlit < 0.56 | |
session_infos = Server.get_current()._session_infos.values() | |
else: | |
session_infos = Server.get_current()._session_info_by_id.values() | |
for session_info in session_infos: | |
s = session_info.session | |
if ( | |
# Streamlit < 0.54.0 | |
(hasattr(s, '_main_dg') and s._main_dg == ctx.main_dg) | |
or | |
# Streamlit >= 0.54.0 | |
(not hasattr(s, '_main_dg') and s.enqueue == ctx.enqueue) | |
or | |
# Streamlit >= 0.65.2 | |
(not hasattr(s, '_main_dg') and s._uploaded_file_mgr == ctx.uploaded_file_mgr) | |
): | |
this_session = s | |
if this_session is None: | |
raise RuntimeError( | |
"Oh noes. Couldn't get your Streamlit Session object. " | |
'Are you doing something fancy with threads?') | |
# Got the session object! Now let's attach some state into it. | |
if not hasattr(this_session, '_custom_session_state'): | |
this_session._custom_session_state = SessionState(**kwargs) | |
return this_session._custom_session_state |
This comment has been minimized.
This comment has been minimized.
Im a little new here and trying to understand. What do you mean by add |
This comment has been minimized.
This comment has been minimized.
This is lovely! I have dynamic sliders in my app (meaning that they change based on a certain user input), and I used this solution to preserve slider values! |
This comment has been minimized.
This comment has been minimized.
For anyone running into errors with this in what are currently nightly release versions, making these changes should fix it:
should change to
and
should change to
|
This comment has been minimized.
This comment has been minimized.
I tried to upgrade to 0.65.1 and run streamlit. When trying to work with the posted SessionState.py i just keep running into: RuntimeError: Oh noes. Couldn't get your Streamlit Session objectAre you doing something fancy with threads? this_session keeps coming up as None. Even in the simplest scenario:
I cleand out all streamlit install in virtual envs or on system level and did a clean install of 0.65.1 including a force-reinstall with pip. I also installed in Docker to make sure of clean environment Any ideas? |
This comment has been minimized.
This comment has been minimized.
It seems like this logic doesnt work anymore with the newest version:
Seems like one now just has to compare on the actual ids in session_info / ctx to get the current session_id of the thread?
|
This comment has been minimized.
This comment has been minimized.
Even quicker if you don't plan to use Streamlit < 0.56, instead of looping through session infos, there's a private method retrieving the correct session_info object based on the report thread's session id. I've been using the following snippet for some time, and it worked before and after 0.65 update. def _get_session():
session_id = get_report_ctx().session_id
session_info = Server.get_current()._get_session_info(session_id)
if session_info is None:
raise RuntimeError("Couldn't get your Streamlit Session object.")
return session_info.session |
This comment has been minimized.
This comment has been minimized.
Thanks @Ghasel, this works fine in my case, while the current method based on this comparison:
was not working with Streamlit -0.65.2, causing the same state to be retrieved from different sessions with cross-talk issues. |
This comment has been minimized.
This comment has been minimized.
Ignore all this, I didn't catch the updates in the gist itself. Those updates fix it. Thanks all! |
This comment has been minimized.
This comment has been minimized.
Thanks @tvst for this nice piece of code https://gist.github.com/FranzDiebold/898396a6be785d9b5ca6f3706ef9b0bc |
This comment has been minimized.
This comment has been minimized.
I wish this would just make it into a stable API already :P |
This comment has been minimized.
This comment has been minimized.
Awesome work and thank you for implementing this! |
This comment has been minimized.
This comment has been minimized.
I was having trouble using this implementation to get the correct session. I have a simple login/logout need for the streamlit dashboard with multiple users. While using the SessionState to remember which user is logged in, the session was always incorrectly identified and the login user was always overwritten by the latest logged user. Which was pretty problematic. I solved it by using from streamlit.report_thread import get_report_ctx
import streamlit as st
class SessionState(object):
def __init__(self, **kwargs):
"""A new SessionState object.
Parameters
----------
**kwargs : any
Default values for the session state.
Example
-------
>>> session_state = SessionState(user_name='', favorite_color='black')
>>> session_state.user_name = 'Mary'
''
>>> session_state.favorite_color
'black'
"""
for key, val in kwargs.items():
setattr(self, key, val)
@st.cache(allow_output_mutation=True)
def get_session(id, **kwargs):
return SessionState(**kwargs)
def get(**kwargs):
"""Gets a SessionState object for the current session.
Creates a new object if necessary.
Parameters
----------
**kwargs : any
Default values you want to add to the session state, if we're creating a
new one.
Example
-------
>>> session_state = get(user_name='', favorite_color='black')
>>> session_state.user_name
''
>>> session_state.user_name = 'Mary'
>>> session_state.favorite_color
'black'
Since you set user_name above, next time your script runs this will be the
result:
>>> session_state = get(user_name='', favorite_color='black')
>>> session_state.user_name
'Mary'
"""
ctx = get_report_ctx()
id = ctx.session_id
return get_session(id, **kwargs) The main drawback of this method is in case the cache is cleared, the state is lost completely. So it's not great but at least works for different users just fine. |
This comment has been minimized.
This comment has been minimized.
I lost track of this thread a while ago, but I think I managed to get session state to do the trick for me using FranzDiebold's alternative implementation. Will update once I get this finally running on a server. |
This comment has been minimized.
This comment has been minimized.
We're working on it right now, actually! There are a few more details we need to nail down, plus a lot of testing, so my guess is we're looking at a January release. |
This comment has been minimized.
This comment has been minimized.
@tvst this is incredible. I know this is not yet production ready, but I'm having trouble explaining why this works to my colleagues. Could you please explain how/why this works at least on a high level? Thanks in advance. |
This comment has been minimized.
This comment has been minimized.
Thanks! Please merge this fix in the gist :] https://gist.github.com/FranzDiebold/898396a6be785d9b5ca6f3706ef9b0bc#file-sessionstate-py-L82
|
This comment has been minimized.
This comment has been minimized.
Thanks! it worked |
This comment has been minimized.
I would add
to the end of this.