Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/plotly/dash into standalo…
Browse files Browse the repository at this point in the history
…ne_dash-renderer
  • Loading branch information
valentijnnieman committed Feb 28, 2019
2 parents 5fbadf1 + 2a4c63e commit 1c59379
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 11 deletions.
6 changes: 3 additions & 3 deletions .circleci/requirements/dev-requirements-py37.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
dash_core_components==0.43.0
dash_core_components==0.43.1
dash_html_components==0.13.5
dash-flow-example==0.0.5
dash-dangerously-set-inner-html
git+git://github.com/plotly/dash-renderer@custom_hooks#egg=dash-renderer
-e git://github.com/plotly/dash-renderer.git@master#egg=dash_renderer
percy
selenium
mock
tox
tox-pyenv
six
plotly==3.6.0
plotly==3.6.1
requests[security]
flake8
pylint==2.2.2
Expand Down
6 changes: 3 additions & 3 deletions .circleci/requirements/dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
dash_core_components==0.43.0
dash_core_components==0.43.1
dash_html_components==0.13.5
dash_flow_example==0.0.5
dash-dangerously-set-inner-html
git+git://github.com/plotly/dash-renderer@custom_hooks#egg=dash-renderer
-e git://github.com/plotly/dash-renderer.git@master#egg=dash_renderer
percy
selenium
mock
tox
tox-pyenv
mock
six
plotly==3.6.0
plotly==3.6.1
requests[security]
flake8
pylint==1.9.4
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
## UNRELEASED
## [0.38.0] - 2019-02-25
## Fixed
- Fix missing indentation for generated metadata.json [#600](https://github.com/plotly/dash/issues/600)
- Fix missing component prop docstring error [#598](https://github.com/plotly/dash/issues/598)
- Moved `__repr__` to base component instead of being generated. [#492](https://github.com/plotly/dash/pull/492)
- Raise exception when same input & output are used in a callback [#605](https://github.com/plotly/dash/pull/605)

## Changed
- Bumped dash-table version from 3.4.0 to [3.5.0](https://github.com/plotly/dash-table/blob/master/CHANGELOG.md#350---2019-02-25)
- Bumped dash-renderer version from 0.18.0 to [0.19.0](https://github.com/plotly/dash-renderer/blob/master/CHANGELOG.md#0190---2019-02-25)

## Added
- Added components libraries js/css distribution to hot reload watch. [#603](https://github.com/plotly/dash/pull/603)
- Callback context [#608](https://github.com/plotly/dash/pull/608)
- Know which inputs fired in a callback `dash.callback_context.triggered`
- Input/State values by name `dash.callback_context.states.get('btn.n_clicks')`

## [0.37.0] - 2019-02-11
## Fixed
Expand Down
3 changes: 3 additions & 0 deletions dash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
from . import exceptions # noqa: F401
from . import resources # noqa: F401
from .version import __version__ # noqa: F401
from ._callback_context import CallbackContext as _CallbackContext

callback_context = _CallbackContext()
35 changes: 35 additions & 0 deletions dash/_callback_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import functools
import flask

from . import exceptions


def has_context(func):
@functools.wraps(func)
def assert_context(*args, **kwargs):
if not flask.has_request_context():
raise exceptions.MissingCallbackContextException(
'dash.callback.{} is only available from a callback!'.format(
getattr(func, '__name__')
)
)
return func(*args, **kwargs)
return assert_context


# pylint: disable=no-init
class CallbackContext:
@property
@has_context
def inputs(self):
return getattr(flask.g, 'input_values', {})

@property
@has_context
def states(self):
return getattr(flask.g, 'state_values', {})

@property
@has_context
def triggered(self):
return getattr(flask.g, 'triggered_inputs', [])
15 changes: 15 additions & 0 deletions dash/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,21 @@ def dispatch(self):

target_id = '{}.{}'.format(output['id'], output['property'])
args = []

flask.g.input_values = input_values = {
'{}.{}'.format(x['id'], x['property']): x.get('value')
for x in inputs
}
flask.g.state_values = {
'{}.{}'.format(x['id'], x['property']): x.get('value')
for x in state
}
changed_props = body.get('changedPropIds')
flask.g.triggered_inputs = [
{'prop_id': x, 'value': input_values[x]}
for x in changed_props
] if changed_props else []

for component_registration in self.callback_map[target_id]['inputs']:
args.append([
c.get('value', None) for c in inputs if
Expand Down
4 changes: 4 additions & 0 deletions dash/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ class ResourceException(DashException):

class SameInputOutputException(CallbackException):
pass


class MissingCallbackContextException(CallbackException):
pass
2 changes: 1 addition & 1 deletion dash/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.37.0'
__version__ = '0.38.0'
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
'Flask>=0.12',
'flask-compress',
'plotly',
'dash_renderer==0.18.0',
'dash_renderer==0.19.0',
'dash-core-components==0.43.1',
'dash-html-components==0.13.5',
'dash-table==3.4.0'
'dash-table==3.5.0'
],
entry_points={
'console_scripts': [
Expand Down
46 changes: 45 additions & 1 deletion tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import dash

from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate, CallbackException
from dash.exceptions import (
PreventUpdate, CallbackException, MissingCallbackContextException
)
from .IntegrationTests import IntegrationTests
from .utils import assert_clean_console, invincible, wait_for

Expand Down Expand Up @@ -759,3 +761,45 @@ def failure(children):
'Same output and input: input-output.children',
context.exception.args[0]
)

def test_callback_context(self):
app = dash.Dash(__name__)

btns = ['btn-{}'.format(x) for x in range(1, 6)]

app.layout = html.Div([
html.Div([
html.Button(x, id=x) for x in btns
]),
html.Div(id='output'),
])

@app.callback(Output('output', 'children'),
[Input(x, 'n_clicks') for x in btns])
def on_click(*args):
if not dash.callback_context.triggered:
raise PreventUpdate
trigger = dash.callback_context.triggered[0]
return 'Just clicked {} for the {} time!'.format(
trigger['prop_id'].split('.')[0], trigger['value']
)

self.startServer(app)

btn_elements = [
self.wait_for_element_by_id(x) for x in btns
]

for i in range(1, 5):
for j, btn in enumerate(btns):
btn_elements[j].click()
self.wait_for_text_to_equal(
'#output',
'Just clicked {} for the {} time!'.format(
btn, i
)
)

def test_no_callback_context(self):
with self.assertRaises(MissingCallbackContextException):
no_context = dash.callback_context.inputs

0 comments on commit 1c59379

Please sign in to comment.