Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent session state behavior when used with widgets #4338

Closed
okld opened this issue Jan 31, 2022 · 8 comments
Closed

Inconsistent session state behavior when used with widgets #4338

okld opened this issue Jan 31, 2022 · 8 comments
Labels
status:needs-triage Has not been triaged by the Streamlit team type:bug Something isn't working

Comments

@okld
Copy link

okld commented Jan 31, 2022

Summary

Session state in Streamlit 0.89.0 to 1.5.0 has a strange behavior when bound to widgets (key parameter).

Steps to reproduce

First case

  1. In the same script run, initialize a session state item, and display a widget bound to it.
  2. Make the widget disappear (by unchecking a checkbox for instance).
  3. Reload with R. ISSUE: Session state item doesn't exist anymore.
  4. Reload again multiple times. ISSUE: Session state is cleared every other run.

Second case

  1. In a first script run, initialize a session state item.
  2. In a second run, display a widget bound to the previous session state item (by checking a checkbox for instance).
    ISSUE: Widget does not take value from session state.
  3. Change widget value, it updates the session state item correctly.
  4. Make the widget disappear.
  5. Reload with R. ISSUE: Session state item resets to its initial value.

Code snippet

Feel free to compare the following script behavior with Streamlit 0.88.0 (working session state) and Streamlit 1.5.0. Both cases are implemented.

import streamlit as st

st.set_page_config(layout="wide")

c1, c2 = st.columns(2)

with c1:
    st.subheader("Widget session state item 1")
    st.write("Session state and widget setup in the same run.")
    st.write("When widget disappear, session state item is cleared every other run.")

    with st.echo(code_location="below"):
        if "widget_bound" not in st.session_state:
            st.session_state.widget_bound = "hello"
            st.success(f"Item added: {st.session_state.widget_bound}")
        else:
            st.info(f"Item exists already: {st.session_state.widget_bound}")

        # Displayed from the beginning by default
        if st.checkbox("Display widget", True):
            st.text_input("Some widget", key="widget_bound")

with c2:
    st.subheader("Widget session state item 2")
    st.write("Session state setup first, then widget in another run.")
    st.write("Widget does not get value initial value from session state.")

    with st.echo(code_location="below"):
        if "widget_unbound" not in st.session_state:
            st.session_state.widget_unbound = "hello"
            st.success(f"Item added: {st.session_state.widget_unbound}")
        else:
            st.info(f"Item exists already: {st.session_state.widget_unbound}")

        # Displayed after session state initialization
        if st.checkbox("Display widget", False):
            st.text_input("Some widget", key="widget_unbound")

Is this a regression?

Yes, session state with widgets worked as expected from version 0.86.0 to 0.88.0.

Debug info

  • Streamlit version: 0.89.0 to 1.5.0
  • Python version: 3.8.6 (pyenv)
@okld okld added type:bug Something isn't working status:needs-triage Has not been triaged by the Streamlit team labels Jan 31, 2022
@andfanilo
Copy link
Contributor

Hmmmm 🤔 interesting

Just passing by and did not read much into it yet, but do you think it's consistent with https://docs.streamlit.io/library/advanced-features/widget-semantics#advanced-notes-on-widget-behavior point 5?

If you don't call a widget function in a script run, we neither store the widget state nor render the widget. If you call a widget function with the same arguments later, Streamlit treats it as a new widget.

So the session state for a disappearing widget would be dropped?

@okld
Copy link
Author

okld commented Jan 31, 2022

So the session state for a disappearing widget would be dropped?

As I understand point 5, the widget state should be dropped if there is no related session state item. The following statement in the documentation seems to confirm this:

[...] When you want to persist widget state for recreating a widget, use Session State to work around [point] 5.

I assume that's what I'm doing here:

if "widget_unbound" not in st.session_state:
    st.session_state.widget_unbound = "hello"

Nonetheless, my session state is either dropped and recreated every other script run (first case), or reset to its initial value (second case).

This behavior is related to a new widget semantic adopted in version 0.89.0 #3827.
There were also some discussions regarding that semantic in #3534.

@okld
Copy link
Author

okld commented Jan 31, 2022

I first encountered this behavior with my multi-page sample code here.

To preserve session state values once the widgets disappear (when going from Settings to Home page), I had to re-apply session state values on themselves.

@willhuang1997
Copy link
Collaborator

Leavin this here for others but as @AnOctopus says,
"These are unfortunate consequences of interactions between [session state as a persistent state mechanism] and [session state as an api for getting and setting widget values out of order], especially with the convenience feature of setting a widget's value in the run it is created (which I have come to regret some because it is inconsistent and confusing).
The solution is generally to not pre-set the widget's value through session state, but instead set the value through the widget's function call as normal, which ensures the value in state is exclusively associated with the widget."

@okld
Copy link
Author

okld commented Feb 2, 2022

Thanks for the answer, I'm closing the issue for now.

@okld okld closed this as completed Feb 2, 2022
@jrieke
Copy link
Collaborator

jrieke commented Feb 22, 2022

Just want to add that I added this discussion to our roadmap for future session state improvements. I think if it really currently works as you pointed out above, this is indeed pretty confusing and something we should change at some point...

@scott-trinkle
Copy link

Chiming in to say I'm having the same issue, it makes it pretty cumbersome to write multi-page apps where different processing and visualizations might appear on different pages based on widget values.

@okld
Copy link
Author

okld commented Mar 4, 2022

Hey @scott-trinkle, maybe you could try the following solution: https://gist.github.com/okld/0aba4869ba6fdc8d49132e6974e2e662

  1. For every widget you want to make persistent, set their key with persist("widget_key").
  2. At the beginning of your script, call load_widget_state()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status:needs-triage Has not been triaged by the Streamlit team type:bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants