Programmable State for Streamlit

The most common thing that I want is form data that I donā€™t want users to have to re-fill out. Iā€™ve made some workarounds by creating a ā€œcacheā€ file and reading/writing from it, but itā€™s clunky and extremely slow for anything more complex.

1 Like

Thanks for your efforts and the opportunity to share my thoughts and ideas on this topic @Thiago. Regarding your question what jumps into my mind are a few things:

In PHP there is something called Sessions which you already mention, maybe it is possible to Cache whether a user is logged in or use some sort of Cookies instead. To admit, I am not to deep into that topic but maybe something like:

@st.cache[ā€œNameā€] = st.text_input(ā€œNameā€)
@st.cache[ā€œPasswordā€] = st.text_input(ā€œPasswortā€ type=ā€œpasswordā€)
if @st.cache[ā€œNameā€] && @st.cache["Password] != Null => @st.cache.Session_start()

from that point onwards users have access to the analytics page until st.cache.Session_end() is called.

Another option I thought about would be a basic database integration with Streamlit to store the credentials. Yet this is just an idea in progress and I did not spend much time thinking about it.

name= st.text_input(ā€œNameā€)
pw = st.text_input(ā€œPasswortā€ type=ā€œpasswordā€)
st.login(name,pw)
=> automatic DB connection and session starts until time expires or
cookies are deleted.

I am looking forward to the way you gonna solve it.

5 Likes

Hej thiago,
I was using one of your ā€œpossible designsā€ to use session states in my application. Unfortunately, the latest update seem to have changed something essential and it is not working anymore with the error: 'Server' object has no attribute '_session_infos'
Could you please point me to what was changed and in case you already know how to solve this?
Thank you,
Matthias

1 Like

@MatthiasPilz You should use sessionState from this link https://gist.github.com/tvst/0899a5cdc9f0467f7622750896e6bd7f

at line 152-156 you can see that _session_infos has been replaced by _session_info_by_id at version 0.56 and above.

3 Likes

I have corporate data and I display various metrics in a monolithic app using a pretty good set of .net tools. I really want to get out of the business of coding up every dashboard item my users come up with.

I too need some form of session management and probably need to be able to display reasonably on desktop and phone.

The biggest ask is that I want possibly dozens of SPAs arranged in a hierarchy and alternatively I want to be able to cycle through the visualizations based on some timing and or alarm conditionsā€¦

I want to configure it as simply as possible, something like dashboard markdown that might build a clickable list of SPA or a list of SPAs that cycle automatically with simple logic.

I would want it to be easy for a vis to decide it didnā€™t need to be shown (rare alarm).

For anyone who is interested, I developed another solution to implementing programmable state by storing state variables for user inputs and even dataframes into a Postgres database. The added benefit with this method is that you can also store objects and binary files and can keep a track of user inputs with timestamps if that is relevant to your application.

Please refer to this tutorial to find more details:

4 Likes

It seems to me that one of the top uses of a state manager is to enable multi-page apps as per this discussion: Multi-page app with session state

However, I believe the solution for multi-page apps needs to be higher level than just a state manager, and potentially different under the hood. The ideal solution wouldnā€™t re-compute all pages, ideally everything would be cached/stateful, i.e. not just widgets (plots, dataframe computations, etc). Itā€™d be the equivalent of doing a simple CSS hide/show. Not sure if you guys are already exploring this, but thought Iā€™d chime in as I think the state manager is too low-level and doesnā€™t cover all the needs in the multi-page use case.

Thank you for the superb work!!

1 Like

My use case is that Iā€™m using Streamlit to build a GUI for the open source network observability tool that Iā€™m building called Suzieq.

I need support for multi-page apps so that users can switch back and forth between pages and not lose their place. Today, there are two different solutions it seems to achieve this: the SessionState code provided by @thiago and the improved one by @FranzDiebold. The second one I know of which actually works well for multi-page apps is the SessionState code by @okld. This latter code works well for my needs albeit being slower than the others because it reruns everything once more after streamlit does its usual rerun. Caching helps, but it shouldnā€™t have to be this way. The former session state code commonly fails to maintain state when switching between pages. I echo what @arturadib wrote in this thread.

Hope this helps,

Dinesh

I was mocking up a demo programmable state API and came up with the following. Figured here would be a good place to post it to get/give ideas.

Cheers,
Simon


import streamlit as st

absolute_zero_shift = 273.25
fahrenheit_gradient = 9.0 / 5.0


def calc_fahrenheit(celsius):
    return fahrenheit_gradient * celsius + 32


def calc_celsius_from_fahrenheit(fahrenheit):
    return (fahrenheit - 32) / fahrenheit_gradient


def calc_celsius_from_kelvin(kelvin):
    return kelvin - absolute_zero_shift


def calc_kelvin(celsius):
    return celsius + absolute_zero_shift


min_value = 0
max_value = 100
step = 1

state = st.session_state.get(
    celsius=min_value,
    fahrenheit=calc_fahrenheit(min_value),
    kelvin=calc_kelvin(min_value),
)

st.slider(
    "Celsius",
    # Build the widget objects to be "state aware", as in, if a state
    # object is passed as a value, have the widget respond automatically
    # to state updates
    value=state.celsius,
    min_value=min_value,
    max_value=max_value,
    step=step,
)

st.slider(
    "Fahrenheit",
    value=state.fahrenheit,
    min_value=calc_fahrenheit(min_value),
    max_value=calc_fahrenheit(max_value),
    step=fahrenheit_gradient * step,
)

# Have the "link_to" function create a directional 'master' graph. Make
# it so that on each UI trigger the links/edges on that master graph are
# traversed, all the while building a record of the traversed links in
# a directional acyclic graph. When traversing these links should any
# traversal along an edge on the 'master' graph causes a cycle, have
# that edge not be traversed for that particular UI interaction.
state.celsius.link_to(state.fahrenheit, calc_fahrenheit)
state.fahrenheit.link_to(state.celsius, calc_celsius_from_fahrenheit)

# Example flow:

# * User triggers the fahrenheit slider
# * state.fahrenheit updated
# * state.celsius to updated by state.fahrenheit -> state.celsius link
#   * importantly, the state.celsius -> state.fahrenheit is not triggered
#     as this is the first step in the chain that produces a cycle in the
#     link graph. These links can be propagated and carried out until no
#     more links remain, or following a link would produce a cycle for
#     that iteration.
# * this triggers celsius slider to update

st.slider(
    "Kelvin",
    value=state.kelvin,
    min_value=calc_kelvin(min_value),
    max_value=calc_kelvin(max_value),
    step=step,
)

state.kelvin.link_to(state.celsius, calc_celsius_from_kelvin)

# Importantly, the directional 'master' graph is defined each run. The
# master graph can change between reruns:
if st.checkbox("Auto update Kelvin?"):
    # Why someone would want to unlink the kelvin slider... I don't
    # know. But... for the example :).
    state.celsius.link_to(state.kelvin, calc_kelvin)


# The state objects can be updated within the Streamlit script by
# calling the "update" method.
if st.button("Make it BOIL!"):
    state.celsius.update(100)


# To access the value within a state object the "value" parameter needs
# to be called.
st.write(
    f"""
        Overview:

        * Celsius: `{state.celsius.value}`
        * Fahrenheit: `{state.fahrenheit.value}`
        * Kelvin: `{state.kelvin.value}`

    """
)

Give us ā€œcallbacksā€ like how they are implemented in dash.

Hello @kart2k15, welcome to the community.

We are open to requests on programmable state, but the feedback would be much better described as Thiago has stated:

Can you rather tell us what your current problem is and how ā€œcallbacksā€ would solve it?

Have a nice day,
Fanilo

1 Like

My ā€œusecaseā€ā€”
User uploads file using file_uploader, selects a seperator/delim from dropdown menu.

The app displays the the first 5 rows, a pandas profile report and a dropdown asking user to select a categorical/discrete column for dimensionality reduction (in my case ad campaign names)

The user selects a column, and I clean it using regex, create a similarity matrix using levenshtein/cosine and then display plotly dendrogram, a input_number element, and another dropdown to select the clustering method (ward, average etc).

The user looks at the dendrogram, decides on the no of cluster & method he/she/they want. I take those inputs run agglomerative clustering on that similarity matrix, and create cluster_df using the cluster labels, and then display the cluster_df

All this needs to be done on a single page! And so far itā€™s been a pain to create it in streamlit!

@kart2k15, maybe the st.cache function can help you to not recalculate everything upon changes.
But feel free to use plotly-dash / Jupyter-voila / bokeh-panel if Streamlit is such a pain.

Hi @kart2k15, thanks a lot for your feedback :slight_smile: very useful to know about your workflow, I can relate to how frustrating it can be to manage this kind of app in Streamlit with lots of cache and unofficial Session state all over the place.

Just to let you know as an ā€œinsiderā€, we are testing callbacks, and trying to judge how well it fits into Streamlitā€™s model. We have to make sure we are not breaking any native widget nor any other Streamlit feature like layout or components, so Iā€™m not guaranteeing youā€™ll have callbacks nor that youā€™ll get them soon, maybe there will be another more magical solutionā€¦but do know that your concerns are heard and I bet there will be announcements soonish, so bear with us with patience :wink:

If you are able to share part of the code for your project, feel free to do so! We would love to look into how we can simplify it with cache or the future version of programmable state.

Thanks for your time,
Fanilo

3 Likes

@maarten-betman I alreadyā€™ve a cache in place to do all that. Also I think the st.cache is way better than the standard lru_cache in functools (which you use in dash btw) since st.cache can hash arrays/dicts as well. Itā€™s passing each and every UI aka ā€œinputā€ element to the SessionState to preserve state is what makes it a pain (plus this ā€œhackā€ doesnā€™t really improve readability either). Peeps have been bringing this issue since 0.56, god know how many gists Iā€™ve had to modify to even get sessionstate working 0.80. And given this issue has been prevalent for some time nowā€” itā€™s naturally expected that streamlit provides a solution by now. This thread alone has been active since Feb, and you guys are still asking me why I need ā€œcallbacksā€ lmao!

@andfanilo thanks for your prompt response. And besides making a featureā€“itā€™d be great if you guys could also push out more tutorials/training for the advanced features (such as cache/SessionState).

2 Likes

@andfanilo My problem since last time has finally been solved through streamlit forms. Much appreciated feature. Thanks for the help

2 Likes

Hey all, :wave:

We have some updates regarding state. Session State is now natively supported in Streamlit via the 0.84.0 release! :partying_face:

To use, upgrade to the latest version:

pip install --upgrade streamlit

Here are some helpful links:

Looking forward to hearing what you think :balloon:

@kart2k15 @maarten-betman @SimonBiggs @ddutt @arturadib @mkhorasani @Chuck_Bass @zhaoooyue @MatthiasPilz @bubthegreat @emal @Dan_Becker @Ian_Calvert @ai_bi_ci @harshjp93

3 Likes

Thanks for the heads up, looking forward to testing it.

Wishing you all happy 4th,

Dinesh

1 Like