Is there a *pretty* way to stop/quit script until parameters have been set?

Basically I want to incorporate multiple checks, so that long computations don’t needlessly launch on their own before the user has carefully considered all the available parameter widgets.

But because I have multiple intermediate steps I don’t want to end up with the pattern

# set parameters, then check box
if widget_condition:
    run()
    the()
    rest()

Because this easily results in

# set some parameters
if widget1_condition:
    run()
    # set some more
    if widget2_condition:
        the()
        # set some more again
        if widget3_condition:
            rest()

So with larger scripts this will easily create an indentation hell.

Instead, I’m currently opting for the not-pattern, so that I can avoid indentation:

# set some parameters, then check box
if not widget1_condition:
    dont_run_script()

# set more parameters, then check box
if not_widget2_condition:
    dont_run_script()

What is dont_run_script()? It’s the good old quit() of course! Paired with strategic caches, this means that a script can only down to the last open “gate”. This means that if my hand slips or streamlit is slow to react, the script doesn’t try to spend 5 minutes on computing with some parameters, and then immediately spends another 5 minutes re-doing it with the new parameters after realizing that the widget was changed.

In practice it works like this:

import streamlit as st

gate_1 = st.checkbox("gate 1", key = "gate 1")
if not gate_1:
    quit()
st.write("Part 1 executed")

gate_2 = st.checkbox("gate 2", key = "gate 2")
if not gate_2:
    quit()
st.write("Part 2 executed")

gate_3 = st.checkbox("gate 3", key = "gate 3")
if not gate_3:
    quit()
st.write("Part 3 executed")

So this works beautifully… except it raises a huge red box. So basically, is there a workaround to intentionally block execution of the remainder without raising an error without encapsulating all blocks into new sub-blocks?

At the moment I’d say your approach is a good way to do it, because there’s presently no directly implemented sense of “collect all of these things before you run something” built into Streamlit.

In discussion within engineering at Streamlit has been the concept of a State object which would keep track of whatever inputs you’d like to keep stateful in between runs.

One solution others have used is the SessionState workaround. A streamlit user also created an alternative implementation of session state here.

The idea would be to use stateful collectors so that you gate the running of the expensive function calls with the contents of the session state.

Regardless, it would help us out if you could comment on this issue regarding client-side statefulness! Streamlit development is being actively shaped by our user base. We want Streamlit to be as useful and beautiful as possible.

Thanks for your question!

1 Like

Hi @komodovaran

Maybe you could put your code in a function an replace quit() with return?

1 Like

That’s an approach I’ve used in several modules. Especially useful for invalid input: Just put out a message with st.warning() and return.

Using SessionState or similar approach can be a very powerful way of getting this behavior when it is combined with the [similarly unofficial] rerun() gist. However you do have to be careful about invoking rerun. Infinite loops are a waiting trap for the unwary :confused:

1 Like

Brilliant, I didn’t think of that!

import streamlit as st

def main():
    gate_1 = st.checkbox("gate 1", key = "gate 1")
    if not gate_1:
        return
    st.write("**Part 1 executed**")

    gate_2 = st.checkbox("gate 2", key = "gate 2")
    if not gate_2:
        return
    st.write("**Part 2 executed**")

    gate_3 = st.checkbox("gate 3", key = "gate 3")
    if not gate_3:
        return
    st.write("**Part 3 executed**")

if __name__ == "__main__":
    main()
3 Likes

Awesome @komodovaran. And thanks for sharing the solution :+1: