Skip to content

Update cycle broken after callback #159

Open
@maxkfranz

Description

@maxkfranz

Description

After running a callback with Output and State on the graph JSON, the update cycle is broken.

This affects Scale AI and Hydro Quebec.

CC: @mtwichan @alexcjohnson @mj3cheun @jackparmer

Steps/Code to Reproduce

This is a minimal example adapted from another reproducible example from Matthew (@mtwichan). You can drag around the nodes in the demo to easily demonstrate the issue:

  1. Press the print-JSON button.
  2. Move a node around.
  3. Press the JSON button again.
  4. Note that the position changed, reflecting the drag.
  5. Press the break button, which has input/output arguments on the JSON.
  6. Move the node around again.
  7. Press the JSON button. The position is not updated this time, or any time after this.

Notes:

  • This is very likely to affect other JSON as well (e.g. data), though position is simple and clear to demonstrate.
  • This may need to be filed in Dash rather than here, as this may be a general issue with Dash.
  • You can drop this file into this repo in the root as something like usage.py to quickly verify.
import dash
import dash_cytoscape as cyto
import dash_html_components as html
from dash.dependencies import Input, Output, State
import json

from dash.exceptions import PreventUpdate

from copy import deepcopy

app = dash.Dash(__name__)
server = app.server

app.layout = html.Div([
    cyto.Cytoscape(
        id='cytoscape',
        elements=[
            {'data': {'id': 'one', 'label': 'Node 1'},
             'position': {'x': 50, 'y': 50}},
            {'data': {'id': 'two', 'label': 'Node 2'},
             'position': {'x': 200, 'y': 200}},
            {'data': {'source': 'one', 'target': 'two', 'label': '1 to 2'}}
        ],
        layout={'name': 'preset'}
    ),
    html.Button("Print elements JSONified", id="button-cytoscape"),
    html.Button("Break", id="button-break"),
    html.Div(id="html-cytoscape"),
])

@app.callback(
    Output("html-cytoscape", "children"),
    [Input("button-cytoscape", "n_clicks")],
    [State("cytoscape", "elements")],
)

def testCytoscape(n_clicks, elements):
    if n_clicks:
        return json.dumps(elements)

@app.callback(
    Output("cytoscape", "elements"),
    [Input("button-break", "n_clicks")],
    [State("cytoscape", "elements")],
)

def breakCytoscape(n_clicks, elements):
    if n_clicks:
        return deepcopy(elements) # happens with a deep copy or not 
    else:
        raise PreventUpdate

if __name__ == '__main__':
    app.run_server(debug=True)

Expected Results

The update cycle should continue.

Actual Results

The update cycle is stopped.

Versions

@mtwichan, would you post your versions here for reference?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions