Skip to content

Commit 1c59379

Browse files
Merge branch 'master' of https://github.com/plotly/dash into standalone_dash-renderer
2 parents 5fbadf1 + 2a4c63e commit 1c59379

File tree

10 files changed

+119
-11
lines changed

10 files changed

+119
-11
lines changed

.circleci/requirements/dev-requirements-py37.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
dash_core_components==0.43.0
1+
dash_core_components==0.43.1
22
dash_html_components==0.13.5
33
dash-flow-example==0.0.5
44
dash-dangerously-set-inner-html
5-
git+git://github.com/plotly/dash-renderer@custom_hooks#egg=dash-renderer
5+
-e git://github.com/plotly/dash-renderer.git@master#egg=dash_renderer
66
percy
77
selenium
88
mock
99
tox
1010
tox-pyenv
1111
six
12-
plotly==3.6.0
12+
plotly==3.6.1
1313
requests[security]
1414
flake8
1515
pylint==2.2.2
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
dash_core_components==0.43.0
1+
dash_core_components==0.43.1
22
dash_html_components==0.13.5
33
dash_flow_example==0.0.5
44
dash-dangerously-set-inner-html
5-
git+git://github.com/plotly/dash-renderer@custom_hooks#egg=dash-renderer
5+
-e git://github.com/plotly/dash-renderer.git@master#egg=dash_renderer
66
percy
77
selenium
88
mock
99
tox
1010
tox-pyenv
1111
mock
1212
six
13-
plotly==3.6.0
13+
plotly==3.6.1
1414
requests[security]
1515
flake8
1616
pylint==1.9.4

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
## UNRELEASED
1+
## [0.38.0] - 2019-02-25
22
## Fixed
33
- Fix missing indentation for generated metadata.json [#600](https://github.com/plotly/dash/issues/600)
44
- Fix missing component prop docstring error [#598](https://github.com/plotly/dash/issues/598)
55
- Moved `__repr__` to base component instead of being generated. [#492](https://github.com/plotly/dash/pull/492)
66
- Raise exception when same input & output are used in a callback [#605](https://github.com/plotly/dash/pull/605)
77

8+
## Changed
9+
- 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)
10+
- 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)
11+
812
## Added
913
- Added components libraries js/css distribution to hot reload watch. [#603](https://github.com/plotly/dash/pull/603)
14+
- Callback context [#608](https://github.com/plotly/dash/pull/608)
15+
- Know which inputs fired in a callback `dash.callback_context.triggered`
16+
- Input/State values by name `dash.callback_context.states.get('btn.n_clicks')`
1017

1118
## [0.37.0] - 2019-02-11
1219
## Fixed

dash/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
from . import exceptions # noqa: F401
55
from . import resources # noqa: F401
66
from .version import __version__ # noqa: F401
7+
from ._callback_context import CallbackContext as _CallbackContext
8+
9+
callback_context = _CallbackContext()

dash/_callback_context.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import functools
2+
import flask
3+
4+
from . import exceptions
5+
6+
7+
def has_context(func):
8+
@functools.wraps(func)
9+
def assert_context(*args, **kwargs):
10+
if not flask.has_request_context():
11+
raise exceptions.MissingCallbackContextException(
12+
'dash.callback.{} is only available from a callback!'.format(
13+
getattr(func, '__name__')
14+
)
15+
)
16+
return func(*args, **kwargs)
17+
return assert_context
18+
19+
20+
# pylint: disable=no-init
21+
class CallbackContext:
22+
@property
23+
@has_context
24+
def inputs(self):
25+
return getattr(flask.g, 'input_values', {})
26+
27+
@property
28+
@has_context
29+
def states(self):
30+
return getattr(flask.g, 'state_values', {})
31+
32+
@property
33+
@has_context
34+
def triggered(self):
35+
return getattr(flask.g, 'triggered_inputs', [])

dash/dash.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,21 @@ def dispatch(self):
968968

969969
target_id = '{}.{}'.format(output['id'], output['property'])
970970
args = []
971+
972+
flask.g.input_values = input_values = {
973+
'{}.{}'.format(x['id'], x['property']): x.get('value')
974+
for x in inputs
975+
}
976+
flask.g.state_values = {
977+
'{}.{}'.format(x['id'], x['property']): x.get('value')
978+
for x in state
979+
}
980+
changed_props = body.get('changedPropIds')
981+
flask.g.triggered_inputs = [
982+
{'prop_id': x, 'value': input_values[x]}
983+
for x in changed_props
984+
] if changed_props else []
985+
971986
for component_registration in self.callback_map[target_id]['inputs']:
972987
args.append([
973988
c.get('value', None) for c in inputs if

dash/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ class ResourceException(DashException):
8080

8181
class SameInputOutputException(CallbackException):
8282
pass
83+
84+
85+
class MissingCallbackContextException(CallbackException):
86+
pass

dash/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.37.0'
1+
__version__ = '0.38.0'

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
'Flask>=0.12',
2121
'flask-compress',
2222
'plotly',
23-
'dash_renderer==0.18.0',
23+
'dash_renderer==0.19.0',
2424
'dash-core-components==0.43.1',
2525
'dash-html-components==0.13.5',
26-
'dash-table==3.4.0'
26+
'dash-table==3.5.0'
2727
],
2828
entry_points={
2929
'console_scripts': [

tests/test_integration.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import dash
1616

1717
from dash.dependencies import Input, Output
18-
from dash.exceptions import PreventUpdate, CallbackException
18+
from dash.exceptions import (
19+
PreventUpdate, CallbackException, MissingCallbackContextException
20+
)
1921
from .IntegrationTests import IntegrationTests
2022
from .utils import assert_clean_console, invincible, wait_for
2123

@@ -759,3 +761,45 @@ def failure(children):
759761
'Same output and input: input-output.children',
760762
context.exception.args[0]
761763
)
764+
765+
def test_callback_context(self):
766+
app = dash.Dash(__name__)
767+
768+
btns = ['btn-{}'.format(x) for x in range(1, 6)]
769+
770+
app.layout = html.Div([
771+
html.Div([
772+
html.Button(x, id=x) for x in btns
773+
]),
774+
html.Div(id='output'),
775+
])
776+
777+
@app.callback(Output('output', 'children'),
778+
[Input(x, 'n_clicks') for x in btns])
779+
def on_click(*args):
780+
if not dash.callback_context.triggered:
781+
raise PreventUpdate
782+
trigger = dash.callback_context.triggered[0]
783+
return 'Just clicked {} for the {} time!'.format(
784+
trigger['prop_id'].split('.')[0], trigger['value']
785+
)
786+
787+
self.startServer(app)
788+
789+
btn_elements = [
790+
self.wait_for_element_by_id(x) for x in btns
791+
]
792+
793+
for i in range(1, 5):
794+
for j, btn in enumerate(btns):
795+
btn_elements[j].click()
796+
self.wait_for_text_to_equal(
797+
'#output',
798+
'Just clicked {} for the {} time!'.format(
799+
btn, i
800+
)
801+
)
802+
803+
def test_no_callback_context(self):
804+
with self.assertRaises(MissingCallbackContextException):
805+
no_context = dash.callback_context.inputs

0 commit comments

Comments
 (0)