St.multiselect with cache? CacheDataAPI.call() got an unexpected keyword argument ‘suppress_st_warning’

Summary

I’m trying to use st.multiselect to append data to a dictionary(dict_options), inserting a new key and using options from a list as values.
When I use text_input, I can create the mulstiselect (with option1 and option2 by default), but when I change the selection (let’s say, I add option3) everything restarts and I miss the multiselect.

Maybe I can use st.cache for this, the problem is when I add @st.cache_data and I try to get a new value, I get a warning:

CachedStFunctionWarning: Your script uses st.multiselect() to write to your Streamlit app from within some cached code at newc(). This code will only be called when we detect a cache “miss”, which can lead to unexpected results.

How to fix this:

  • Move the st.multiselect() call outside newc().
  • Or, if you know what you’re doing, use @st.cache_data(suppress_st_warning=True) to suppress the warning

If I feel brave and I use @st.cache_data(suppress_st_warning=True) I get an error:

TypeError: CacheDataAPI.call() got an unexpected keyword argument ‘suppress_st_warning’

Steps to reproduce

Code snippet:

import streamlit as st

#@st.cache_data(suppress_st_warning=True)
def newc(_col2, dict_options, new_country, options):
    dict_options[new_country] = ['option1','option2']
    dict_options[new_country] = _col2.multiselect(f'Select the options you want for {new_country}', options, dict_options.get(new_country), disabled=False)
    return dict_options, new_country
    
def main():
    dict_options = {}
    options = ['option1','option2','option3','option4']
    col1, col2, col3 = st.columns([8, 18, 1])
    new_country = col1.text_input('New country', value='UK', label_visibility="collapsed")
    if col1.button('Add New Country'):
        dict_options, new_country = newc(col2, dict_options, new_country, options)

if __name__== "__main__" :
    main()

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

  • Run the code snippet
  • Just clic on the ‘Add new country’ button and try to add ‘option3’ or ‘option4’.
  • Try to uncomment the decorator on line 3, with or without suppress_st_warning=True you’ll get different warnings/errors.

Expected behavior:

I would like to add a new dictionary key:value pair, for example
UK : ['option1','option2','option3']

Actual behavior:

Well, one of the biggest issues for me with streamlit: it reloads and I miss the new data, and so far I just don’t know how to use st.cache with multiselect… the code is written, I’m pretty sure I’m close, I just need some help.

Debug info

  • Streamlit version: (get it with $ streamlit version) 1.19.0
  • Python version: (get it with $ python --version) 3.11.2
  • Using Conda? PipEnv? PyEnv? Pex? Nope
  • OS version: windows 11
  • Browser version: Firefox 110.0.1

Hi @mabusdogma :wave:

Thank you for sharing a reproducible example! There are a couple of things going on here :thinking:

What you can do is use a form to capture user input for a new country and options. When the form is submitted, call the newc function to add the new country to the dictionary. I’m pretty sure there are better ways to do this, but here’s the first approach that came to mind:

import streamlit as st

# Abbreviate the session state object for convenience
state = st.session_state

# Define a function to add a new country with options to a dictionary
def newc(_col2, new_country, options, state):
    # Set the new country as a key in the session state dictionary and initialize its value to ["option1", "option2"]
    state.dict_options[new_country] = ["option1", "option2"]
    # Create a multiselect dropdown to select options for the new country
    # The dropdown is initialized with the options currently associated with the new country in the dictionary
    state.dict_options[new_country] = _col2.multiselect(
        f"Select the options you want for {new_country}",
        options,
        state.dict_options.get(new_country),
        disabled=False,
    )
    # Write the current contents of the dictionary to the app
    _col2.write(state.dict_options)

# If the dictionary is not already in the session state, create an empty dictionary
if "dict_options" not in state:
    state.dict_options = {}

# Create a list of available options for the multiselect dropdown
options = ["option1", "option2", "option3", "option4"]

# Create three columns for the app layout
col1, col2, col3 = st.columns([8, 18, 1])

# Use a form to capture user input for a new country and options
# When the form is submitted, call the newc function to add the new country to the dictionary
with col1.form("my_form"):
    # Create a text input for the new country
    new_country = st.text_input("New country", value="UK", key="new_country")
    # Call the newc function to create the multiselect dropdown for the new country
    newc(col2, new_country, options, state)
    # Create a form submit button to update the dictionary with the new country and options
    submit_button = st.form_submit_button(label="Add new country")

multiselect-form

Is this the behavior you were looking for?

1 Like

Thank you very much for your quick answer!
It’s almost done! I just two more questions:

  • In the way it is, it seems that UK is already added no matter what, and maybe I don’t want to add UK, it was just an example. Could I stop adding the country until I press the Add new country button?

  • Is there a way to hide the multiselect box until I press the Add new country button?

I have tried on_click and if - else statements, but when I use them, it starts to reload the page :frowning:

Hey @mabusdogma :wave:

Sorry for the wait!

Here’s the code rewritten to satisfy both those constraints:

import streamlit as st

state = st.session_state

if "dict_options" not in state:
    state.dict_options = {}

if "submitted" not in state:
    state.submitted = False

options = ["option1", "option2", "option3", "option4"]

col1, col2, col3 = st.columns(3)

with col1.form("my_form"):
    new_country = st.text_input("New country")
    submit_button = st.form_submit_button(
        label="Add new country", on_click=lambda: state.update(submitted=True)
    )

if state.submitted:
    state.dict_options[new_country] = col2.multiselect(
        f"Select the options you want for {new_country}",
        options,
        default=options[:2],
    )
    col2.write(state.dict_options)

multiselect-form

2 Likes

It works perfect!
Thank you very much @snehankekre !!! :100:

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.