Can anyone please help resolve this error message? TypeError: store_data() takes 1 positional argument but 2 were given

UPDATE: I was able to solve part of the problem by changing the name of the last variable returned as “data.”

But I still have not resolved the big problem yet.


I am hoping to use dcc.Store to resolve a significant problem.

For some reason, my app is returning the output (final values) from a previous user session, as if it were cached in the browser somewhere unknown.

I need to figure out how to exercise greater control over where the data from each session is stored and when it is cleared. But my efforts over the last two days have failed.

By using dcc.Store, I had hoped to solve the problem by setting the ‘storage_type’ argument to ‘memory’ and changing the ‘clear_data’ argument’s value to True with a callback at the end of the program.

Thank you for reviewing my question.

This is my dcc.Store code, which resides at the tail end of the dbc.Accordion component:

# dcc.Store inside the user's current browser session
    dcc.Store(id='store-data', data=[], storage_type='memory', clear_data=False) # 'local' or 'session'
]) # Later use callback to chang clear_data to True when final analysis is completed.

Here is the Traceback:

Traceback (most recent call last):
  File "C:\Users\rober\ings\venv\Lib\site-packages\dash\_callback.py", line 151, in add_context
    output_value = func(*func_args, **func_kwargs)  # %% callback invoked %%
TypeError: store_data() takes 1 positional argument but 2 were given

I am not entirely clear on why I receive this message, except that I DO have two callbacks with data as the Output.

But one of those callbacks is the one takes the user input and stores it in the data_key in dcc.Store.

I will present you with those callbacks as the minimal, meaningful examples. Below, I pasted the code from the entire page below just in case someone wishes to review it.

Here is the first callback that uses “data” in the Output statement:

# This callback and function accepts the input and processes the recipe,
# producing an editable table of amounts, units of measurement,
# ingredients and modifiers essential to a nutritional profile.

# Important Note: The user-edited data must be resaved as valid_recipe_ents.json.

@callback(
    Output('nutrients-review-datatable', 'data'),
    Input('recipe-textarea-submit-button', 'n_clicks'),
    State('recipe-textarea', 'value')
)
def update_output(n_clicks, value):
    if n_clicks > 0:
        print("Recipe submitted")
        with open("temp/recipe_contents.txt", "w", encoding='utf-8') as recipe_contents:
            recipe = value.split('\n')
            for line in recipe:
                recipe_contents.write(line)
                recipe_contents.write('\n')
        convert_unicode_fractions()
        convert_regular_fractions()
        prepare_recipe_text()
        parse_recipe_ner()
        validate_recipe_ents()
        prepare_data_for_table()

        with open("./json/recipe_ents_list.json", "r") as final_recipe_file:
            data = json.loads(final_recipe_file.read())

            return data

Here is the second callback that uses “data” in the Output statement:

# This callback takes the edited data from the datatable
# and stores that data in the dcc.Store item at the 
# base of the accordion.

@callback(
    Output('store-data', 'data'),
    Input('nutrients-review-datatable-submit-button', 'n_clicks'),
    State('nutrients-review-datatable', 'data'),
    prevent_initial_call=True
)
def store_data(data):
    # hypothetical enormous dataset with millions of rows
    return dataset.to_dict('records')

Again, I appreciate your time and assistanhce.

Sincerely,

Robert

============================================================
JUST IN CASE SOMEONE WANTS TO SEE THE ENTIRE PAGE.

dash.register_page(__name__, path='/')

layout = html.Div(
    [
        html.Br(),
        html.H5("Welcome to Ingredible's Nutrition Assistant"),
        html.Br(),
        dbc.Accordion(
            [
                dbc.AccordionItem( # Step 1: Paste your recipe here.
                    [
                        html.Div("Please paste your recipe here.", className="mb-4"),
                        dcc.Textarea(
                            id="recipe-textarea",
                            value="",
                            style={"width": "100%", "height": 400},
                            placeholder="Please note: All information in brackets or parantheses will be deleted.",
                            className="mb-4"
                        ),
                        dbc.Button(
                            "Submit Recipe", 
                            id="recipe-textarea-submit-button",
                            className="me-2", 
                            n_clicks=0
                        ),
                        dbc.Button(
                            "Reset", 
                            id="recipe-textarea-reset-button",
                            className="me-2", 
                            n_clicks=0
                        ),
                        html.Div(id="MSG-1"),
                    ],
                    title="Step 1",
                    item_id="acc_textarea",
                ),
               dbc.AccordionItem( # Step 2: Edit initial results.
                    [
                        html.Div("Please edit initial results.", className="mb-4"),
                        dash_table.DataTable(
                            id="nutrients-review-datatable",
                            columns=[{'id': i, 'name':i} for i in ['Index', 'ID', 'Grams', 'Amounts', 'Modifiers', 'Units', 'Ings', 'Multiplier']],
                            style_cell={'textAlign': 'left', 'padding': '10px', 'fontSize':14, 'font-family': 'sans-serif', 'fontWeight': 'bold'},
                            style_header={'backgroundColor': '6eafde', 'fontWeight': 'bold', 'font-size': 14, 'border': '1px solid black', 'align': 'left', 'color': 'black'},
                            style_data = {'color': 'black'},
                            style_data_conditional=[{'if': {'row_index': 'odd'}, 'backgroundColor': 'rgb(248,248,248)'}],
                            data=None,
                            editable=True,                           
                            row_deletable=True,
                            #tooltip,
                            hidden_columns=['Index', 'ID', 'Grams', 'Total Grams', 'Final Multipliers'],
                            css=[{"selector": ".show-hide", "rule": "display: none"}],
                            sort_action='native',
                            page_action='native',
                            page_size=10,
                            style_table={'height': '400px', 'overflowY': 'auto'},
                        ),
                            dbc.Button(
                            "Submit Edits", 
                            id="nutrients-review-datatable-submit-button",
                            className="me-2", 
                            n_clicks=0
                        ),
                            dbc.Button(
                            "Reset", 
                            id="recipe-textarea-reset-button",
                            className="me-2", 
                            n_clicks=0
                        ),
                        html.Div(id="MSG-2"),
                    ],
                    title="Step 2",
                    item_id="acc_datatable",
                ),
                dbc.AccordionItem(# Step 3: Review final report.
                    [
                        html.Div("Review final analysis.", className="mb-4"),
                        html.Div(id="final-nutritional-profile-output", className="mb-4"),
                    ],
                    title="Step 3",
                    item_id="acc_report",
                ),
            ],
            id="accordion",
            active_item="acc_textarea",
            flush=True,
        ),
        html.Div(id="accordion-contents", className="mt-3"),
        # dcc.Store inside the user's current browser session
    dcc.Store(id='store-data', data=[], storage_type='memory', clear_data=False) # 'local' or 'session'
]) # Later use callback to chang clear_data to True when final analysis is completed.


# @app.callback(
#     Output("accordion-contents", "children"),
#     [Input("accordion", "active_item")],
# )
# def change_item(item):
#     return f"Item selected: {item}"

# This callback and funtion resets the text area value
# to empty when the user clicks the reset button.

@callback(
    Output("recipe-textarea", "value"),
    [Input("recipe-textarea-reset-button", "n_clicks")],
    prevent_initial_call=True
)
def reset_textarea(n_clicks):
    if n_clicks > 0:
        return ""

# This callback and function accepts the input and processes the recipe,
# producing an editable table of amounts, units of measurement,
# ingredients and modifiers essential to a nutritional profile.

# Important Note: The user-edited data must be resaved as valid_recipe_ents.json.

@callback(
    Output('nutrients-review-datatable', 'data'),
    Input('recipe-textarea-submit-button', 'n_clicks'),
    State('recipe-textarea', 'value')
)
def update_output(n_clicks, value):
    if n_clicks > 0:
        print("Recipe submitted")
        with open("temp/recipe_contents.txt", "w", encoding='utf-8') as recipe_contents:
            recipe = value.split('\n')
            for line in recipe:
                recipe_contents.write(line)
                recipe_contents.write('\n')
        convert_unicode_fractions()
        convert_regular_fractions()
        prepare_recipe_text()
        parse_recipe_ner()
        validate_recipe_ents()
        prepare_data_for_table()

        with open("./json/recipe_ents_list.json", "r") as final_recipe_file:
            data = json.loads(final_recipe_file.read())

            return data

# This callback takes the edited data from the datatable
# and stores that data in the dcc.Store item at the 
# base of the accordion.

@callback(
    Output('store-data', 'data'),
    Input('nutrients-review-datatable-submit-button', 'n_clicks'),
    State('nutrients-review-datatable', 'data'),
    prevent_initial_call=True
)
def store_data(data):
    # hypothetical enormous dataset with millions of rows
    return dataset.to_dict('records')
    
    # 2. or save as string like JSON
    # return dataset.to_json(orient='split')

# This function and callback accept the user's edits to the nutritional
# elements in the table and produce a nutritional profile. In the future, check boxes
# allow the user to tailor the report, tracking 50+ nutrients.

@callback(
    Output('final-nutritional-profile-output', 'children'),
    Input('nutrients-review-datatable-submit-button', 'n_clicks'),
    State('nutrients-review-datatable', 'data'),
    prevent_initial_call=True
)

def create_nutritional_profile(n_clicks, data):
    if n_clicks > 0:

        finalize_ing_dicts(data)
        create_match_idslist()
        create_nutrient_dicts()
        merge_nutrient_to_ing_dicts()
        results = final_conversions()

        for each_dict in results:
            for k,v in each_dict.items():
                print(k, v)

        df = pd.read_csv('final_charts_data.csv')
        # round numbers to two decimal places
        dff = df.round(decimals = 2)
        # supress/hide certain columns
        dfff = dff.drop(['Gender', 'Ages', 'Benchmarks'], axis=1)
        # unique values only
        data = dfff.drop_duplicates(subset=['Nutrients'])

        return dbc.Table.from_dataframe(data, id='review-final-analysis-datatable',
            class_name="me-2", 
            striped=True, 
            bordered=True, 
            hover=True, 
            style={"width": "1200px"} )


if __name__ == "__main__":
    app.run_server(debug=True, port=2000)

Hi @robertpfaff,

The problem is that your callback has one input and one state, but you have just a single argument in the function. This should solve the issue:

@callback(
    Output('store-data', 'data'),
    Input('nutrients-review-datatable-submit-button', 'n_clicks'),
    State('nutrients-review-datatable', 'data'),
    prevent_initial_call=True
)
def store_data(n_clicks, dataset):
    return dataset

Please also note that dataset comes from a “data” prop in a DataFrame which is already a dict.

1 Like

You again! Yes, that did it. I still cannot convince the code to discard the previous session’s values, which is messing up my final report. But I also have not yet had time to change the value of clear_data to True.

Thank you…again.

About your issue with unclear cached data: I looked in the complete code that you posted and I couldn’t find any components with persistence where session data could be stored, other than the dcc.Store itself. However dcc.Store is not actually used anywhere…

I suspect that your problem is that you are creating some files inside the callbacks using side-effects from functions. One example is final_charts_data.csv that you import on the last callback. This is a potential problem because if anything goes wrong in your “pipeline”, the file won’t update and you’ll see results from a previous run. If I understand your app correctly, this would be the case if the user goes straight to the last accordion item, push the button with an empty data and somehow the file does not update. Note that this has nothing to do with caching or persistence, it would happen with two distinct users in different browsers.

In summary, it is very risky to rely on files to communicate data between callbacks. That’s why dcc.Store exists. Well, not only that, but you get my gist.

I agree with just about everything you wrote here, but may I ask, what do you mean that dcc.Store data is not actually used anywhere?

I am not offended, not at all. But the answer to that question might tell me what I am doing wrong. The dcc.Store component is set up correctly (yes?) inside the dbc.Accordion component. Do I need a callback/function that places the data in the Store after the initial processing? Is that what I am missing? That was my current thinking.

I watched the video on sharing callbacks several times, and I just don’t remember. I have a neurological injury called small-fiber neuropathy and I struggle with the memory issues mostly rooted in side-effects of medications.

I completely agree the problem probably lies in the use of files to transfer information, and I plan to make those changes this week. That point is very well taken. When I started learning Python eight months ago, it was part of a brain/memory strengthening exercise plan. This particular passion project just happened to capture my imagination. So the code has evolved as I have evolved. There’ still a lot of beginner’s assumptions in there.

The final_charts_data csv file should be completely re-written - over the previous session’s code - every time I run the program, shouldn’t it? When I have deleted it, in an attempt to see if it was the problem, the new file still retains the results of the previous session, which baffles me. I have also tried deleting other files without solving the problem.

As you wrote, there still could be some “side-effects” from this (admittedly beginner’s) attempt to pass the recipe data from function to function, and I working on updating that approach. If the user goes straight to the third Accordion Item, he sees nothing. I agree he should see the results of the previous session, but he doesn’t. In step two, he still has the opportunity to edit/approve the information after the initial processing.

So the report does not appear unless the user approves the initial results in step two.

I still agree its “very risky to rely on files to communicate data between callbacks.”

Anyway, I want to thank for all the feedback. It means a lot to me, it really does.

Take care,

Robert

You have a strong grasp of how my program functions:

  1. User pastes a recipe into the text area, then clicks submit button

  2. Data is tagged for ingredients, amounts, units of measurement and modifiers (e.g. small, large) and presented to the user in an editable datatable. User makes edits if necessary and clicks submit button.

  3. Thanks to the magic of Natural Language Processing and USDA databases, the code analyses each ingredient for nutritional content and returns a nutritional profile in a report (dbc.Table.from_dataframe)

  4. Because its now a multipage app, the user can also view charts (e.g. nutritional content vs. RDAs), review the source data and any other bell or whistle I decide to add.

First of all, please don’t take offense because I was just trying to emphasize that the problem wasn’t necessarily in the Store component. Maybe the wording was wrong, but I had absolutely no foul intend.

The reason why I made the comment you have highlighted is that you do have the Store component as a callback Output (according to the first post in thread), however you are not using it in any other callback as Input or State, therefore the caching issue is not there.

The final_charts_data csv file should be completely re-written - over the previous session’s code - every time I run the program, shouldn’t it? When I have deleted it, in an attempt to see if it was the problem, the new file still retains the results of the previous session, which baffles me. I have also tried deleting other files without solving the problem.

I wouldn’t take it for granted, but I don’t know which function creates it and in which callback it is invoked. I will maintain my recommendation of switching the files to Store components, where it is easier for you to inspect the behaviour when they enter and leave the callback.

Don’t be discouraged by these issues, caching and persistence are quite sophisticated things and you already came a very long way with your app! Keep it up the nice work! :slight_smile:

1 Like

Not offended at all! I crave the feedback.

“…however you are not using it in any other callback as Input or State,”

I think I just had a minor epiphany. I thought I was using that data because data is the Output in the main callback, but I need to include it as the Input, don’t I? This is one of those “duh” moments.

That’s what’s wrong, isn’t it?
,
P.S. I just tried running the program in a new browser - one I had never used before - and it still produces the same, messed-up results. That suggests the problem is not "cached’ memory, but bad math.