Repository navigation
Dash renderer doesn't allow cycles in callbacks #730
Description
@chriddyp : as you requested in the community discussion on interdependent components, here is an issue.
The example below shows a use case for keeping a slider and an input box in sync which
relies on having a cycle in callbacks. The cycle is broken when the values are the same.
The current behavior in dash-renderer is to throw an error when the callback graph detects a cycle (which is hard to decipher short of looking at console logs as per #228)
This error is generated by dependecy-graph module. However the changelog shows that as of v0.7.0 it now allows a circular:True keyword to suppress the error.
It's relatively easy to change dependencyGraph.js in dash-renderer to always allow circular graphs (as shown in the diff with my forked branch)
What's more difficult is to allow the setting to be changed as a configuration more upstream. It's not clear to me how that would be done. My guess is that it needs to be passed as an argument to bundle.js (something like bundle.js?v=13.1&circular=true) which dependecyGraph.js needs to be able to interpret. To generate that URL, that would require changing the dash._collect_and_register_resources to append (on top of the version number) config parameters.?
Another option is to put the setting in app.config (say app.config['allow_circular_callbacks']) and
modify dash._config() to dump that in id="_dash-config". But it's not clear to me how that would then get passed to bundle.js ?
If someone wants to help me with exposing that functionality happy to do a PR.
Sample code which works with my forked dash-renderer repo but fails with the current release:
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import time
import uuid
import numpy as np
import logging
logging.basicConfig(format='%(levelname)s|%(asctime)s|%(name)s|\t %(message)s',level=logging.WARNING)
log = logging.getLogger('test')
log.setLevel(logging.INFO)
app = dash.Dash()
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = \
html.Div([
html.Div(style={'display':'inline-block','width':300}, children = [
html.Label('Frequency'),
dcc.Slider(id='freq_slider',value=1,min=0,max=5,step=0.1, marks={i: '{}'.format(i) for i in range(6)}),
]),
html.Hr(),
dcc.Input(id='freq_box', value=1, type='number'),
html.Hr(),
dcc.Graph(id='graph')
])
@app.callback(Output('freq_box', 'value'),
[Input('freq_slider', 'value')])
def updateBox(slider_val):
return slider_val
@app.callback(Output('freq_slider', 'value'),
[Input('freq_box', 'value')],
[State('freq_slider', 'value')])
def updateSlider(box_val, slider_val_old):
#Box and slider are different, update the slider
if box_val != slider_val_old:
return box_val
#Otherwise, no need to update slider so we raise
#PreventUpdate to break the cycle
#Note that if other callbacks rely on components involvwd in the cycle,
#raising PreventUpdate() might cause one of those callbacks
#not to fire
raise dash.exceptions.PreventUpdate()
@app.callback(Output('graph', 'figure'),
[Input('freq_slider', 'value')])
def updateGraph(freq):
t = np.linspace(0,10,1000)
y = np.sin(2*np.pi*freq*t)
data = [dict(type='scatter', x = t, y = y)]
layout = dict(type='scatter', mode='lines')
return dict(data=data, layout=layout)
if __name__ == '__main__':
app.run_server(debug=True, port=8080)
Activity
chriddyp commentedon Sep 12, 2018
So, even if the JS allows it (i.e. it doesn't raise a JS error), does it end up triggering the necessary updates? Does it do one cycle, or multiple cycles? I suspect that this is a much deeper issue than just making the JS error go away, but if everything "just works" then that's a big win.
To accept this changed, I'd need to see a set of integration tests that demonstrate that a set of callbacks with circular dependencies do indeed fire the necessary callbacks (circularly).
It it doesn't end up triggering the cycle of callbacks, then I'd rather keep the error message as it is indeed an error. In plotly/dash-renderer#64, we're bubbling up the JS error message to the app, so the developer should become aware that dash doesn't allow this functionality (rather than it being "silently" thrown in the JavaScript console).
emilhe commentedon Nov 29, 2018
I keep running into this problem. Is there any progress and/or workarounds?
vtslab commentedon Mar 31, 2019
I published a workaround at:
http://yaaics.blogspot.com/2019/03/circular-references-in-plotlydash.html
As stated in the blog, allowing intended circular references that can be broken with raise PreventUpdate is still the prefered final solution.
Fix number inputs for IE11 (plotly#730)
Fix number inputs for IE11 (#730)
gvwilson commentedon Jul 17, 2024
Hi - we are tidying up stale issues and PRs in Plotly's public repositories so that we can focus on things that are most important to our community. If this issue is still a concern, please add a comment letting us know what recent version of our software you've checked it with so that I can reopen it and add it to our backlog. (Please note that we will give priority to reports that include a short reproducible example.) If you'd like to submit a PR, we'd be happy to prioritize a review, and if it's a request for tech support, please post in our community forum. Thank you - @gvwilson