Skip to content

Callbacks outputing DataTable data evaluated as invalid multi-output callback #666

Description

@Marc-Andre-Rivet

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

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions