How can I ensure my function execute in the correct order?

This is a rookie question. I’ve completed writing my first Python app (took months).

I am bringing it together in Dash, converting from Streamlit.

Now, I am suddenly receiving an error message I did not receive in Streamlit, as if the functions are running concurrently. I know that’s not possible in Python, but I am receiving mysterious error messages like this one:

"File “C:\Users\rober\ings\clean_recipe_text.py”, line 14, in clean_recipe_text
with open(“temp/fraction_free_recipe.txt”, “r”, encoding=‘utf-8’) as decimal_recipe:
FileNotFoundError: [Errno 2] No such file or directory: ‘temp/fraction_free_recipe.txt’
PS C:\Users\xxxxx\ings> "

This file does not exist because it cannot exist unless I submit a recipe, which it is not letting me do. Instead, it is running the last function in the code (so far) and looking for a file that does not exist yet. This has never happened before, and I am wondering have I missed something obvious? Or is this issue particular to Dash for some reason?

I’ve tried some solutions like forcing the function to wait until the submit button has been clicked at least once before it runs the code inside the functions, but that does not solve the problem. I would greatly appreciate any constructive feedback. I’ve decided to post the code below though it may seem like too much.

It was producing the correct results up until I added the last function.

app.layout = html.Div([

    dcc.Textarea(

        id='textarea-state-example',

        value='',

        style={'width': '100%', 'height': 200},

        placeholder='Please note: Information inside brackets or parantheses will be deleted.'

    ),

    html.Button('Submit', id='textarea-state-example-button', n_clicks=0),

    html.Div(id='textarea-state-example-output',

             style={'whiteSpace': 'pre-line'})

])

@app.callback(

    Output('textarea-state-example-output', 'children'),

    Input('textarea-state-example-button', 'n_clicks'),

    State('textarea-state-example', 'value')

)

def update_recipe(n_clicks, value):

    if n_clicks > 0:

        with open("temp/recipe_contents.txt", "w", encoding='utf-8') as recipe_contents:

            recipe = value.split('\n')

            for line in recipe:

                print(line)

                recipe_contents.write(line)

                recipe_contents.write('\n')

            return "Recipe contents written to file."

def convert_fractions(n_clicks):

    if n_clicks > 0:

        vulgar_fractions = convert_unicode_fractions()

        common_fractions = convert_regular_fractions()

        print("Output: Vulgar Fractions:")

        print(vulgar_fractions)

        time.sleep(10)

        print("")

        print("Output: Common Fractions:")

        print(common_fractions)

        time.sleep(10)

        print("")

def clean_recipe_textacy(n_clicks, filename):

    if n_clicks > 0:
        clean_recipe = clean_recipe_text()
        print("Output: Clean Recipe Text:")
        print(clean_recipe)
        time.sleep(10)
        print("")


clean_recipe_textacy('temp/fraction_free_recipe.txt')

if __name__ == '__main__':
    app.run_server(debug=True, dev_tools_silence_routes_logging=False)

Hi,

I think you are mixing how different streamlit and dash approaches are. I don’t have a lot of experience with the former, but I know that streamlit executes code in a serial manner like a regular python script.

Dash is different in the sense each callback will be (by default) executed when the application starts and then every time one of the inputs get updated (plus some edge cases not important now). The order in which the callbacks are called is not related to the order they are written in the file, but instead depend on a dependency tree created by dash when the app runs. That’s why you have the impression of concurrency.

In your particular example, your first function is defined as a callback, i.e. it has the @app.callback decorator on top of it and it does work as a callback when the app runs. Your two other functions don’t have the decorator and will be completely ignored by the app when it runs. Then you invoke the function in the botton, which is executed even before the app starts and will never be executed again (not a callback).

It can be a bit unnatural Dash’s approach compared to streamlit, so I hope this helps you. Please feel free to follow up if something isnt clear.

1 Like

Oh, yes. This helps a lot with defining the problem. I sincerely appreciate your response. I guess the solution means I need to spend some quality time learning about callbacks and dependency trees? Would studying up on callbacks be the best way to get the problem solved? Beyond reading the docs, can you recommend a resource?

Thanks again.

I think reading the docs and going through its examples is enough in the beginning. This is how I learned it at least.

If you prefer videos, @adamschroeder has a highly reputed series of videos on Youtube about Dash.

1 Like