Session State for Streamlit 🎈

It looks like the issue is tied to using a pandas dataframe columns attribute as the options for the multi select. I fixed the issue by changing changing it into a list. Not sure why this broke in 0.84, but it’s working for me now!

Thanks for your patience @Squaddaffi . We have an idea and figured out a workaround in the meantime. I believe you should use list(df.columns) for now here. Essentially, we call the .index standard method on lists on a dataframe index, which doesn’t exist. It should be fixed in the next release. We are investigating how exactly this came into existence to put out the correct fix. We so appreciate your kindness and guidance and sorry for the breakage that was. Let me know if the workaround does not work or if there are any other questions!

1 Like

@Squaddaffi I have opened a new github issue for the problem you reported. It should be an easy fix, once I have determined exactly which part of the system’s incorrect assumptions needs to be changed. Thank you for letting us know about it.

This is really a game changing for Streamlit. I cannot wait to use it. Congratulations

3 Likes

had you try to restart your streamlit app after upgrade?

Hello, thanks for this work around. It seems this does not work for my situation.

Any possible solutions or is waiting the only plausible solution for now?

This is an awesome update, thanks so much! Was waiting for this :partying_face:

Is there a way Streamlit can now expand on this session state management to set secure cookies?
That way we can keep people logged in even after they’ve shut down their browser and set some data when they reopen the page :slightly_smiling_face:

Streamlit is getting more epic with every new update, big kudos to the team!

3 Likes

@Squaddaffi The issue has been fixed with 0.84.1 release along with a couple other bugs. Let us know if you run into any issues.

Love the idea @Fhireman ! I will be sure to share this with our product team

1 Like

Awesome, thanks for sharing the idea with the product team Ken!
Really appreciate that you guys listen to community suggestions :+1:

I think being able to deal with user sessions will be very welcome for people who use it as a webapp framework.

Each update there are less things left on my wishlist for Streamlit to be the perfect framework for both my own webapp ideas and for it to be interesting for the ML team at my job :smiley:

My ultimate Streamlit wishlist would be:

  • Secure cookies to keep people logged in & set data when reopening the page with their token
  • Streamlit deploy(or serve) CLI command that removes the hamburger menu and running animation(+ stop button) from the top-right so non-technical users just see the page and elements on it without extras(I use Streamlit to help teach Python programming to beginners and it would save them some cognitive complexity as to what’s going on).
  • Image or text alignment arguments e.g. st.image(image, halign=‘center’, valign=‘center’) so layouts don’t seem off when trying to have a centered round image above a piece of text or some radio buttons.
  • A more suitable navigation menu. Maybe a list of stateful buttons instead of radio buttons since they are bigger/easier to click on mobile when navigating to a different “page”. Clicking radio buttons to go to another page doesn’t feel natural to non-technical users(I’ve ran some field tests :stuck_out_tongue:).

Then I’d be one happy Streamlit camper! :partying_face:

Nicetohavesbutnotextremelynecessary would be:

  • Choose where the st.sidebar lives, left or right. A way to make it work horizontally as a navigation bar at the top and bottom would be amazing and opens up a lot of different layout options.
  • Be able to pass and set bg colors and text colors(hex/hsl?) to individual Streamlit widgets would open up creativity on top of the default awesome Streamlit style. (I know about the st.markdown unsafe, allow_html=True workaround but it would be great to have this in a more Streamlitty way like st.button(color=“hsl(0,50,100)”, text_color="#fff", name=“specialbutton”) and link the custom css style to the name value.
2 Likes

Just out of curiosity since I tried it before with the previous hacky sessionstate object and now with the new session state but both without success:
Is it possible to dynamically change the list of radio buttons for navigation after a session state is set?

i.e.: I’ve got a login page on the right, a sidebar on the left with one radio button for navigation named Login. The login page is the default page when someone opens the app.

After successful login a session_state.logged_in gets set and the navigation in the sidebar changes to two buttons (page one, page two) and the page on the right should show the page one widgets(so no more login screen).
When switching to page two it should show page two widgets(here it reverts to the default login radio button again which I didn’t expect since the session_state is set).
The login page and radio button option should be completely gone until the browser refreshes.

So far I’ve gotten half of it working and had some “not iterable” ValueErrors(even when trying to convert to a list after retrieving values from session_state) when using a key=“navoptions” argument with my radiobuttons widget. Where navoptions was set in the session_state to a list with [“pageone”, “pagetwo”].

Any ideas? :grinning:

Hey @Fhireman

I think a small code example that demonstrates this would be easier to debug. I was thinking a key would only be necessary (or perhaps a different key based on the logged in status). The ValueErrors would need to be checked but it’s possible they are triggered because it interprets the value to be “login” but it’s no longer available in the list of options.

Having a code example would be easier to look through.

1 Like

Good news :smiley:

We have something like that planned, which would let you store data per-user in a secure way. Implementation is at least a few quarters away, though.

Something like that is coming soon. Probably by end of year.

This is on our roadmap, but not yet scheduled. We’re still thinking about the best solution here.

Something like that is coming soon. Probably by end of year. Maybe early 2022.

2 Likes

Wow Thiago that’s not only good news, that’s great news! :smiley:
I forgot to add my Flask-style routes wish to the wishlist for different endpoints but the things that you mentioned would already make it so good that I’ll probably only rarely miss it.

Can’t wait to see what awesome stuff is coming up the next quarters! :partying_face:

Edit: I got the dynamic updating to work by moving the selection var/radio buttons menu to the bottom and making it one widget instead of redefining the same widget variable.
Though I still can’t find a way to clear the login screen widgets. It just adds the st.write from pageone to the login page and only removes the login widgets when I switch to pagetwo(going back to pageone after that also works and only shows pageone widgets, it’s just the initial “on submit” on the login screen that doesn’t work as expected).
Is it possible to clear all widgets on successful submit of the form?

Hey Ken,

Of course :slight_smile: without code you don’t know what I’m doing haha.
The code example is a bit of a mess now since I’ve tried everything from saving current selection to state, to trying to clear the login page widgets with a callback and trying different orders, etc, etc.

I’ve gotten to the point that switching to pagetwo and back to pageone after login it ends up the way I want it, but now I just need to get rid of that login radio button on successful login and show the pageone widget.

import streamlit as st

def loginPage():

    test_users = {"test":"test"}

    st.markdown("### Welcome to a demoapp :shopping_bags:")
    st.markdown("### Use the menu on the left(top-left arrow on mobile) to navigate! :smile:")

    with st.form(key='login_form'):
        if "username" not in st.session_state:
            username = st.text_input("Username or e-mail")
            password = st.text_input("Password", type="password")
        else:
            username = st.text_input("Username or e-mail")
            password = st.text_input("Password", type="password")
        submit_button = st.form_submit_button(label="Submit")

    if submit_button:
        if username in test_users:
            if test_users[username] == password:
                st.success("Succesfully logged in! :tada:")
                st.session_state.logged_in = True
                st.session_state.navopts = ["pageone", "pagetwo"]
                st.session_state.username = username
                return True
            else:
                st.error("Aww something went wrong! Couldn't log you in :cold_sweat:")
                return False
        else:
            st.error("Aww something went wrong! Couldn't log you in :cold_sweat:")
            return False

def pageOne():
    st.write("Seems like you've logged in!")

selection = None

if "navopts" not in st.session_state:
    st.session_state.navopts = ["login"]
    selection = st.session_state.navopts

if "logged_in" not in st.session_state:
    selection = st.sidebar.radio("", st.session_state.navopts)
    if loginPage():
        st.session_state.navopts = ["pageone", "pagetwo"]
    
if "logged_in" in st.session_state and st.session_state.logged_in == True:
    selection = st.sidebar.radio("", st.session_state.navopts)

if selection == "pageone":
    pageOne()

Hope it’s readable!

2 Likes

My example application for a secure Hydralit multi-page app has a login form at the front door. You can see the login page code by expanding the “Show App Code” expander on the page. Can also see the host application code by expanding using “Show Host Code”. The entire example code is located here.

Hydralit-secure-example

3 Likes

Thanks for sharing, this is cool stuff! :smiley:
I’ve just got my login example to work except I have to press submit one more time after successful login to get rid of the login page.
Actually thought of using the time.sleep after successful login as well to show the success message before refreshing.
How did you implement the self.do_redirect() function(what does it do underneath)? I can’t see if it’s a Hydralit specific function or a Streamlit one.

It’s a Hydralit function that comes from the HydraHeadApp template class. The simple version of what it does is, set session state variable to the target app, then run st.experimental_rerun().

2 Likes

Awesome! The st.experimental_rerun() function was all I needed to get my idea working! Thanks a lot :smiley: Session state and the rerun function work great together.

Hydralit looks lit :fire: by the way, I’ll definitely play around with it.
I’m trying to find a way to call the Web Share API on button click to easily share Streamlit app data on mobile(to Telegram or Whatsapp for instance), any chance that’s already implemented in Hydralit?

1 Like

Would you mind showing how you are using st.experimental_rerun() in your case?

I am trying to use it, but keeps re-runnig endlessly. :thinking: :thinking: