Simultaneous multipage widget state persistence, data_editors with identical contents, and multiprocessing capability

This is as much a confirmation from Streamlit developers as I think it might be a good reference for Streamlit users who have similar needs as I do. Here is my solution for simultaneously providing for the three functionalities mentioned in the title above:

Home.py:

import streamlit as st

def main():

    selectbox_options = [1, 2, 3]

    if 'my_selectbox_key' not in st.session_state:
        st.session_state['my_selectbox_key'] = selectbox_options[0]

    for key, val in st.session_state.items():
        if not key.endswith('__do_not_persist'):
            st.session_state[key] = val

    st.title('Home page')

    st.selectbox('Select from options:', selectbox_options, key='my_selectbox_key')

if __name__ == '__main__':
    main()

pages/a_different_page.py:

import streamlit as st

def dummy_function_calling_multiprocessing_functionality():
    pass

def main():

    # (1) Code like the following is needed to preserve widget state on different Pages
    # Can't use "st.session_state.update(st.session_state)" because it will include all widgets including st.data_editor widgets which would throw a "StreamlitAPIException: Values for st.button, st.download_button, st.file_uploader, st.data_editor, st.chat_input, and st.form cannot be set using st.session_state." error
    for key, val in st.session_state.items():
        if not key.endswith('__do_not_persist'):
            st.session_state[key] = val

    st.title('Another page')

    # (2) Different keys in the data editors are needed to prevent a "DuplicateWidgetID: There are multiple identical st.data_editor widgets with the same generated key." error
    st.write('One directory listing:')
    st.session_state['data_editor1_edited'] = st.data_editor(['a', 'b', 'c'], key='data_editor1__do_not_persist')
    st.write('Another directory listing (same exact contents):')
    st.session_state['data_editor2_edited'] = st.data_editor(['a', 'b', 'c'], key='data_editor2__do_not_persist')

    dummy_function_calling_multiprocessing_functionality()

# (3) Needed for performing multiprocessing functionality such as using Python's "multiprocessing" module
if __name__ == '__main__':
    main()

I would argue that the ability to provide all three of these functionalities (summarized below), while perhaps not originally intended by the Streamlit creators/developers, is important for developing robust workflows that can be managed by a nice Streamlit interface.

Streamlit developers including @blackary, @okld, @mathcatsand, @Goyo, can you please confirm that the above is the correct way to achieve all three of these functions simultaneously: (1) multipage widget state persistence, (2) data_editors with identical contents, and (3) multiprocessing capability?

Thank you, and regardless, thanks Streamlit creators/developers/moderators for all your hard work and such a great product!

Referencing posts:

I am not a developer and you are asking about three features I am not very familiar with. Multipages are too rigid for my taste and I feel more comfortable using functions and radio buttons or select boxes to dynamically change the UI. Data editors work in a different way from other widgets and so far I haven’t had to use them. Multiprocessing is a big topic by itself and your example is quite unspecific about it.

I can’t see anything obviously wrong in your example, that is all I can say.

Looks right to me. I wasn’t previously aware of the need for the if __name__ == "__main__" when using multiprocessing, but it does indeed seem to be required to avoid RuntimeErrors. The other two examples are good ones, though #2 could be more generalized to just say “if you have otherwise identical widgets, you need to give them different keys”.

Thanks for looking it over @Goyo! I hear you on the multipages though our team can’t live without them with the size of our workflows… that is, once we figured out the correct code block at the top, which was key for loading the session state between them. Your example (linked above, and the __name__ == "__main__" part in particular) for using multiprocessing was crucial for getting that working as expected. Much appreciated!

Thanks for confirming @blackary! Yes that __name__ == "__main__" part seemed to save us many headaches. Good point about generalizing the statement for #2. Sort of tied into #1, I was also trying to make the point of “not just different keys, but keys that for certain widgets should not saved to the session state,” which we achieved by slightly modifying your suggested code from one of your posts (linked above). Thanks again for looking this over and for all your help!

1 Like

FWIW, my toy example also works if you unconditionally call main(), without the if __name == "__main__": part. That is something I almost always do in scripts because it allows me to use them as library modules, which may be convenient for testing and debugging, but I don’t see how it is related to multiprocessing.

Interesting, thanks @Goyo. I’m pretty surprised to hear you found that multiprocessing works correctly without the if __name__ == "__main__" part. It seems to be required per Python’s multiprocessing docs here (see the first code example at the top). Multipages aside, I suppose I could see it working if the main Streamlit script you run has the if __name__ == "__main__" condition and from there you call the main() function of a module containing multiprocessing instructions. Regardless, that’s a good idea, to wrap the “main” functionality in a function so you can import it elsewhere!

Actually it works but I just noticed unexpected messages appearing in the console at random:

Stopping...
Stopping...
Stopping...
Stopping...

I don’t know what that means but yeah, better avoid it by following the advice in the documentation.

Thanks @Goyo, yes that’s exactly what I got without the if __name__ == '__main__'! :slight_smile:

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