dash
dash copied to clipboard
[BUG] background callback with `running` argument causes type error
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.
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?
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.
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.
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.
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.
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?
Hey @Coding-with-Adam! Yes, I am using pattern matching for example to structure code with All-in-One Components.
In that case it could be related to this issue: https://github.com/plotly/dash/issues/2681
Duplicate of #2111