-
-
Save tvst/036da038ab3e999a64497f42de966a92 to your computer and use it in GitHub Desktop.
"""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 |
There is another gist related to sessions in Streamlit here.
(( I decided to remove this reference. Better to see the following message from @okld ))
I used this implementation from distinct browsers without issues. However I am not using in production, so I don't know anything about performance, memory, etc.
Hope this helps...
The main goal of that session state you shared is to fix a rollback issue when you try to synchronize widget values with session state values.
Here's my original post on this implementation and how to use it: https://discuss.streamlit.io/t/multi-page-app-with-session-state/3074
One caveat with it though, if you use it with widgets, you won't be able to store objects that Streamlit cannot hash.
Soon we'll get an official session state implementation which's going to be way better than what we have for now.
Meanwhile, in case anyone want a simple implementation which supports setting default values, but don't need to sync widget values with session state:
from streamlit.report_thread import get_report_ctx
from streamlit.server.server import Server
from types import SimpleNamespace
def get_state(**default_values):
session_id = get_report_ctx().session_id
session_info = Server.get_current()._get_session_info(session_id)
if session_info is None:
raise RuntimeError("Could not get your Streamlit session instance.")
if not hasattr(session_info.session, "_custom_state"):
session_info.session._custom_state = SimpleNamespace(**default_values)
return session_info.session._custom_state
# Retrieve state and define default values once
state = get_state(my_default_number=2, my_default_string="hello")
# Assign a value to your state every run
state.my_value = "world"
print(
state.my_default_number,
state.my_default_string,
state.my_value
)
Thanks !!
Let me ask... It seems you are the original author of the code but... are you the same person of the reference I've shared ?
When I first searched for something about sessions in Streamlit, I have found Thiago's implementation and the other.
Thiago's implementation didn't work when two users opened the application, so I took the other one.
By the way, I have tested the code you shared above, using three users and it worked perfectly (thanks again).
I'm someone else
I'm not sure if it was discussed before. But when I use your @tvst SessionState the cached value is shared among multiple opened web pages or users. The change caused by one user will be visible to other users. How can we make SessionState to maintain the values per user. You can verify this by opening up two web pages with a sider value cached by a session state object. Change of slider value in page A will be visible in page B whenever it refreshes. (cc: @okld)
If you want to store regular values in your session state, you can try the code snippet above.
If you want to bind widget values with session state, try this implementation instead.
If you want to store regular values in your session state, you can try the code snippet above.
If you want to bind widget values with session state, try this implementation instead.
Thanks a lot! that works! @okld
As @okld said, you should not use this Gist anymore! It has been replaced by an official feature of Streamlit, called st.session_state 🥳
@tvst how to make session_state save to disk ,and recover state when re-run?
I made a hack by adding attribute to current server object , hope it is okay or will I get some performance issues in futute? @tvst