dash icon indicating copy to clipboard operation
dash copied to clipboard

[BUG] background callback with `running` argument causes type error

Open seb-schulz opened this issue 2 years ago • 7 comments

Describe your context

dash                 2.14.1
dash-core-components 2.0.0
dash-html-components 2.0.0
dash-table           5.0.0
  • if frontend related, tell us your Browser, Version and OS

    • OS: MacOS 13.5.2
    • Browser: Chrome
    • Version: 119

Describe the bug

Here is an example to demonstrate my example:

import os
import dash
from dash import html, dcc
import time

if 'REDIS_URL' in os.environ:
    # Use Redis & Celery if REDIS_URL set as an env variable
    from celery import Celery
    celery_app = Celery(__name__,
                        broker=os.environ['REDIS_URL'],
                        backend=os.environ['REDIS_URL'])
    background_callback_manager = dash.CeleryManager(celery_app)

else:
    # Diskcache for non-production apps when developing locally
    import diskcache
    cache = diskcache.Cache("./cache")
    background_callback_manager = dash.DiskcacheManager(cache)

app = dash.Dash(
    __name__,
    background_callback_manager=background_callback_manager,
)


class ids:
    button = dict(a="button_id")
    paragraph = dict(a="paragraph_id")


app.layout = html.Div([
    html.P(id=ids.paragraph, children=["Button not clicked"]),
    html.Button(id=ids.button, children="Run Job!"),
    html.Button(
        id=dict(a='broken_button'),
        # id='broken_button',
        children="This button should be disabled while callback is running",
    ),
])


@dash.callback(
    dash.Output(ids.paragraph, "children"),
    dash.Input(ids.button, "n_clicks"),
    running=[[dash.Input(dict(a='broken_button'), "disabled"), True, False]],
    # running=[[dash.Input('broken_button', "disabled"), True, False]],
    background=True,
    prevent_initial_call=True,
)
def update_clicks(*args):
    print('started', args)
    time.sleep(1)
    print('ended', args)
    return [
        f"Clicked with following args: {args!r}\ndash version: {dash.__version__}"
    ]


if __name__ == "__main__":
    app.run(debug=True)

Expected behavior

Console of the developer tools shows the following error:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'concat')
    at getInputHistoryState (reducer.js:57:36)
    at reducer.js:82:34
    at reducer.js:121:16
    at dispatch (redux.js:288:1)
    at index.js:20:1
    at callbacks.ts:209:9
    at Array.forEach (<anonymous>)
    at sideUpdate (callbacks.ts:206:22)
    at callbacks.ts:338:25

When the id is type of string instead of a dictionary, this example works as expected.

seb-schulz avatar Nov 07 '23 13:11 seb-schulz

Thanks for reporting this bug, @seb-schulz . Running your code, I get the same error message in the console. But I couldn't get the example to work as expected when the id is of type string. Can you please share the full code that made the example work?

Coding-with-Adam avatar Nov 07 '23 14:11 Coding-with-Adam

Hey @Coding-with-Adam, here is a working example:

import os
import dash
from dash import html, dcc
import time

if 'REDIS_URL' in os.environ:
    # Use Redis & Celery if REDIS_URL set as an env variable
    from celery import Celery
    celery_app = Celery(__name__,
                        broker=os.environ['REDIS_URL'],
                        backend=os.environ['REDIS_URL'])
    background_callback_manager = dash.CeleryManager(celery_app)

else:
    # Diskcache for non-production apps when developing locally
    import diskcache
    cache = diskcache.Cache("./cache")
    background_callback_manager = dash.DiskcacheManager(cache)

app = dash.Dash(
    __name__,
    background_callback_manager=background_callback_manager,
)


class ids:
    button = dict(a="button_id")
    paragraph = dict(a="paragraph_id")


app.layout = html.Div([
    html.P(id=ids.paragraph, children=["Button not clicked"]),
    html.Button(id=ids.button, children="Run Job!"),
    html.Button(
        # id=dict(a='broken_button'),
        id='not_broken_button',
        children="This button should be disabled while callback is running",
    ),
])


@dash.callback(
    dash.Output(ids.paragraph, "children"),
    dash.Input(ids.button, "n_clicks"),
    # running=[[dash.Input(dict(a='broken_button'), "disabled"), True, False]],
    running=[[dash.Input('not_broken_button', "disabled"), True, False]],
    background=True,
    prevent_initial_call=True,
)
def update_clicks(*args):
    print('started', args)
    time.sleep(1)
    print('ended', args)
    return [
        f"Clicked with following args: {args!r}\ndash version: {dash.__version__}"
    ]


if __name__ == "__main__":
    app.run(debug=True)

The difference is just using 'not_broken_button' instead of dict(a='broken_button') as an id.

seb-schulz avatar Nov 07 '23 16:11 seb-schulz

Thanks for sharing, @seb-schulz . I'm actually still getting a console error with your code, albeit a different one, which is tied to the first button.

Uncaught DOMException: Failed to execute 'querySelector' on 'Document': 'label[for="{"a":"button_id"}"]' is not a valid selector.

image

I was able to remove this error by converting the id of the first button to a string as well, instead of the dict you used. html.Button(id="button_id", children="Run Job!"), dash.Input("button_id", "n_clicks"),

I'm assuming the reason for these errors is related.

Coding-with-Adam avatar Nov 08 '23 15:11 Coding-with-Adam

Hey @Coding-with-Adam, thank you for sharing your findings. It looks like your finding is caused by a chrome extension. Does my second example work when you disable some of those extensions?

In Chrome 118 + 119 and Firefox 115, my first example is broken and my second example is working.

seb-schulz avatar Nov 08 '23 20:11 seb-schulz

hi @seb-schulz I labeled this issue as a bug. Are you using the dict(a="button_id") type because you're using pattern matching callbacks?

Coding-with-Adam avatar Nov 10 '23 17:11 Coding-with-Adam

Hey @Coding-with-Adam! Yes, I am using pattern matching for example to structure code with All-in-One Components.

seb-schulz avatar Nov 11 '23 22:11 seb-schulz

In that case it could be related to this issue: https://github.com/plotly/dash/issues/2681

mbschonborn avatar Dec 18 '23 10:12 mbschonborn

Duplicate of #2111

T4rk1n avatar Mar 28 '24 20:03 T4rk1n