Skip to content

Commit d3753db

Browse files
authored
Merge pull request #680 from plotly/partial-update
Partial updates
2 parents f2c56d1 + 1689606 commit d3753db

File tree

5 files changed

+171
-47
lines changed

5 files changed

+171
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Support for "Clientside Callbacks" - an escape hatch to execute your callbacks in JavaScript instead of Python [#672](https://github.com/plotly/dash/pull/672)
44
- Added `dev_tools_ui` config flag in `app.run_server` (serialized in `<script id="_dash-config" type="application/json">`)
55
to display or hide the forthcoming Dev Tools UI in Dash's front-end (dash-renderer). [#676](https://github.com/plotly/dash/pull/676)
6+
- Partial updates: leave some multi-output updates unchanged while updating others [#680](https://github.com/plotly/dash/pull/680)
67

78
## [0.40.0] - 2019-03-25
89
### Changed

dash/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .dash import Dash # noqa: F401
1+
from .dash import Dash, no_update # noqa: F401
22
from . import dependencies # noqa: F401
33
from . import development # noqa: F401
44
from . import exceptions # noqa: F401

dash/dash.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@
7474
_re_renderer_scripts_id = re.compile(r'id="_dash-renderer')
7575

7676

77+
class _NoUpdate(object):
78+
# pylint: disable=too-few-public-methods
79+
pass
80+
81+
82+
# Singleton signal to not update an output, alternative to PreventUpdate
83+
no_update = _NoUpdate()
84+
85+
7786
# pylint: disable=too-many-instance-attributes
7887
# pylint: disable=too-many-arguments, too-many-locals
7988
class Dash(object):
@@ -1053,15 +1062,25 @@ def add_context(*args, **kwargs):
10531062
)
10541063

10551064
component_ids = collections.defaultdict(dict)
1065+
has_update = False
10561066
for i, o in enumerate(output):
1057-
component_ids[o.component_id][o.component_property] =\
1058-
output_value[i]
1067+
val = output_value[i]
1068+
if val is not no_update:
1069+
has_update = True
1070+
o_id, o_prop = o.component_id, o.component_property
1071+
component_ids[o_id][o_prop] = val
1072+
1073+
if not has_update:
1074+
raise exceptions.PreventUpdate
10591075

10601076
response = {
10611077
'response': component_ids,
10621078
'multi': True
10631079
}
10641080
else:
1081+
if output_value is no_update:
1082+
raise exceptions.PreventUpdate
1083+
10651084
response = {
10661085
'response': {
10671086
'props': {

tests/IntegrationTests.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,23 @@ def percy_snapshot(cls, name=''):
2121
name=snapshot_name
2222
)
2323

24-
def wait_for_element_by_css_selector(self, selector):
25-
return WebDriverWait(self.driver, TIMEOUT).until(
26-
EC.presence_of_element_located((By.CSS_SELECTOR, selector))
24+
def wait_for_element_by_css_selector(self, selector, timeout=TIMEOUT):
25+
return WebDriverWait(self.driver, timeout).until(
26+
EC.presence_of_element_located((By.CSS_SELECTOR, selector)),
27+
'Could not find element with selector "{}"'.format(selector)
2728
)
2829

29-
def wait_for_text_to_equal(self, selector, assertion_text):
30-
return WebDriverWait(self.driver, TIMEOUT).until(
31-
EC.text_to_be_present_in_element((By.CSS_SELECTOR, selector),
32-
assertion_text)
30+
def wait_for_text_to_equal(self, selector, assertion_text, timeout=TIMEOUT):
31+
el = self.wait_for_element_by_css_selector(selector)
32+
WebDriverWait(self.driver, timeout).until(
33+
lambda *args: (
34+
(str(el.text) == assertion_text) or
35+
(str(el.get_attribute('value')) == assertion_text)
36+
),
37+
"Element '{}' text was supposed to equal '{}' but it didn't".format(
38+
selector,
39+
assertion_text
40+
)
3341
)
3442

3543
@classmethod

0 commit comments

Comments
 (0)