Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] background callback with running argument causes type error #2685

Closed
seb-schulz opened this issue Nov 7, 2023 · 8 comments
Closed
Labels
bug something broken sev-3 annoyance with workaround

Comments

@seb-schulz
Copy link

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.

@Coding-with-Adam
Copy link

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?

@seb-schulz
Copy link
Author

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.

@Coding-with-Adam
Copy link

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.

@seb-schulz
Copy link
Author

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.

@Coding-with-Adam Coding-with-Adam added bug something broken sev-3 annoyance with workaround P1 needed for current cycle P4 and removed P1 needed for current cycle labels Nov 10, 2023
@Coding-with-Adam
Copy link

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?

@seb-schulz
Copy link
Author

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

@mbschonborn
Copy link

In that case it could be related to this issue: #2681

@T4rk1n
Copy link
Contributor

T4rk1n commented Mar 28, 2024

Duplicate of #2111

@T4rk1n T4rk1n marked this as a duplicate of #2111 Mar 28, 2024
@T4rk1n T4rk1n closed this as completed Mar 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken sev-3 annoyance with workaround
Projects
None yet
Development

No branches or pull requests

4 participants