Dash: Callbacks outputing DataTable `data` evaluated as invalid multi-output callback

Created on 27 Mar 2019  路  3Comments  路  Source: plotly/dash

The following app exhibits this behavior with dash==0.40.0

# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash_table import DataTable

from dash.dependencies import Input, Output, State

import pandas as pd

from datetime import date, datetime, timedelta
import time

url = 'https://github.com/plotly/datasets/raw/master/26k-consumer-complaints.csv'

rawDf = pd.read_csv(url)
df = rawDf.to_dict("rows"),

app = dash.Dash()
app.scripts.config.serve_locally = True

app.layout = html.Div(children=[
    html.Button(
        id='button-update',
        children=['Update']
    ),
    dcc.DatePickerRange(
        id='my-date-picker-range',
        min_date_allowed=date(2018, 12, 14),
        max_date_allowed=date.today(),
        start_date=date.today() - timedelta(days=7),
        end_date=date.today(),
        display_format='D/M/Y'
    ),
    dcc.Loading(
        id="loading-1",
        children=[
            DataTable(
                id='datatable-weapons',
                columns=[{"name": i, "id": i, "type": "numeric", 'format': {'locale': {'group': '.', 'decimal': ','}}} for i in rawDf.columns],
                data=[]
            )
        ]
    )])

@app.callback(
    [Output('my-date-picker-range', 'start_date'), Output('my-date-picker-range', 'end_date')],
    [Input('button-update', 'n_clicks')])
def update_output(n_clicks):
    if n_clicks is not None and n_clicks > 0:
        start_date = date(2019, 2, 12)
        end_date = date.today()
        return start_date, end_date

    return date(2019, 2, 23), date(2019, 2, 27)

@app.callback(
    [Output('datatable-weapons', 'data')],
    [Input('my-date-picker-range', 'start_date'), Input('my-date-picker-range', 'end_date')])
def update_graph(begin_dt, end_dt):
    begin_date = datetime.strptime(begin_dt, '%Y-%m-%d')
    end_date = datetime.strptime(end_dt, '%Y-%m-%d')
    days = (end_date - begin_date).days

    rawDfSlice = rawDf[0:days]
    dfSlice = rawDfSlice.to_dict("rows")

    time.sleep(5)

    return (dfSlice,) # This value needs to be wrapped to be accepted by Dash

if __name__ == "__main__":
    app.run_server(port=8053)

return (dfSlice,) above works, but normal usage return dfSlice causes this error:

[2019-03-27 09:58:18,898] ERROR in app: Exception on /_dash-update-component [POST]
Traceback (most recent call last):
  File "/Users/marc-andrerivet/.pyenv/versions/release-py37/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/marc-andrerivet/.pyenv/versions/release-py37/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/marc-andrerivet/.pyenv/versions/release-py37/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/marc-andrerivet/.pyenv/versions/release-py37/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/Users/marc-andrerivet/.pyenv/versions/release-py37/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/marc-andrerivet/.pyenv/versions/release-py37/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/marc-andrerivet/projects/release/dash/dash/dash.py", line 1073, in dispatch
    response.set_data(self.callback_map[output]['callback'](*args))
  File "/Users/marc-andrerivet/projects/release/dash/dash/dash.py", line 986, in add_context
    len(output_value)
dash.exceptions.InvalidCallbackReturnValue: Invalid number of output values for ..datatable-weapons.data...
 Expected 1 got 4

Returning a plain array also exhibits the problem:

return [{ 'prop1': 'value1'}, { 'prop1': 'value2 }]

Did I get this wrong somehow?

Suggested approach
One approach could be to not validate the size of the output tuple in cases where the callback returns a single output. This will provide an experience consistent with the previous versions of Dash, not introduce a breaking change in behavior, provide a consistent coding experience throughout all the props/callbacks, and not break the abstraction between Dash usage and implementation details on our side.

What this approach loses in validation "perfectness" is more than made up from noise reduction in real usage scenarios. In any case, prop validation can easily pick up the slack from this "incorrectness". Put another way, giving priority to external facing consistency vs. internal facing consistency.

Tagging @T4rk1n

Most helpful comment

If you wrap your output definition in an array:

@app.callback(
    [Output('datatable-weapons', 'data')],

then you also have to wrap your return in an array / tuple:

return (dfSlice,)

If you don't wrap the output definition:

@app.callback(
    Output('datatable-weapons', 'data'),

then you don't wrap your return:

return dfSlice

All 3 comments

After testing this further and discussing this with @T4rk1n and @alexcjohnson, I agree that this is neither a regression nor a bug. Closing. Will open another issue for multi-output allowing an empty array of outputs.

hi @Marc-Andre-Rivet,
i'v got similar error ,and also need to wrap the output in a tuple as return (out_array, )
can you share why this is not a bug and why have to write it this way?

If you wrap your output definition in an array:

@app.callback(
    [Output('datatable-weapons', 'data')],

then you also have to wrap your return in an array / tuple:

return (dfSlice,)

If you don't wrap the output definition:

@app.callback(
    Output('datatable-weapons', 'data'),

then you don't wrap your return:

return dfSlice
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Chris8080 picture Chris8080  路  3Comments

KyleKing picture KyleKing  路  4Comments

lochbrunner picture lochbrunner  路  3Comments

ncdingari picture ncdingari  路  4Comments

germayneng picture germayneng  路  4Comments