Skip to content

Commit 1c75712

Browse files
authored
Fix update method on empty and uninitialized array property (#1092)
* Fix for GH1072. Empty array properties are replaced during update * Don't error when a literal property is set to it's correct value. This avoids errors when performing updates like figure.update(data=[go.Scatter(...), ...]) * Fix two failing validator tests
1 parent 7ab0c90 commit 1c75712

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+253
-108
lines changed

_plotly_utils/basevalidators.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,16 +1658,21 @@ class LiteralValidator(BaseValidator):
16581658
"""
16591659
Validator for readonly literal values
16601660
"""
1661-
def __init__(self, plotly_name, parent_name, **kwargs):
1662-
super(LiteralValidator, self).__init__(plotly_name=plotly_name,
1663-
parent_name=parent_name,
1664-
**kwargs)
1661+
def __init__(self, plotly_name, parent_name, val, **kwargs):
1662+
super(LiteralValidator, self).__init__(
1663+
plotly_name=plotly_name,
1664+
parent_name=parent_name,
1665+
**kwargs)
1666+
self.val = val
16651667

16661668
def validate_coerce(self, v):
1667-
raise ValueError("""\
1668-
The '{plotly_name}' property of {parent_name} is read-only""".format(
1669-
plotly_name=self.plotly_name, parent_name=self.parent_name
1670-
))
1669+
if v != self.val:
1670+
raise ValueError("""\
1671+
The '{plotly_name}' property of {parent_name} is read-only""".format(
1672+
plotly_name=self.plotly_name, parent_name=self.parent_name
1673+
))
1674+
else:
1675+
return v
16711676

16721677

16731678
class ImageUriValidator(BaseValidator):

_plotly_utils/tests/validators/test_basetraces_validator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ def validator():
1010
'bar': 'Bar',
1111
'box': 'Box'},
1212
plotly_name='prop',
13-
parent_name='parent')
13+
parent_name='parent',
14+
set_uid=True)
1415

1516

1617
# Tests

_plotly_utils/tests/validators/test_infoarray_validator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def validator_number3_free():
3030
# ----------
3131
# ### Acceptance ###
3232
@pytest.mark.parametrize('val', [
33-
[1, 'A'], ('hello', 'world!'), [1, ()], [-1, 1]
33+
[1, 'A'], ('hello', 'world!'), [1, set()], [-1, 1]
3434
])
3535
def test_validator_acceptance_any2(val, validator_any2: InfoArrayValidator):
3636
coerce_val = validator_any2.validate_coerce(val)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import pytest
2+
from _plotly_utils.basevalidators import LiteralValidator
3+
import numpy as np
4+
5+
6+
# Fixtures
7+
# --------
8+
@pytest.fixture()
9+
def validator():
10+
return LiteralValidator('prop', 'parent', 'scatter')
11+
12+
13+
# Tests
14+
# -----
15+
# ### Acceptance ###
16+
@pytest.mark.parametrize('val', ['scatter'])
17+
def test_acceptance(val, validator):
18+
assert validator.validate_coerce(val) is val
19+
20+
21+
# ### Test rejection ###
22+
@pytest.mark.parametrize('val',
23+
['hello', (), [], [1, 2, 3], set(), '34'])
24+
def test_rejection(val, validator):
25+
with pytest.raises(ValueError) as validation_failure:
26+
validator.validate_coerce(val)
27+
28+
assert 'read-only' in str(validation_failure.value)

codegen/datatypes.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,12 @@ def __init__(self""")
254254
for literal_node in literal_nodes:
255255
lit_name = literal_node.name_property
256256
lit_parent = literal_node.parent_path_str
257-
lit_val = literal_node.node_data
257+
lit_val = repr(literal_node.node_data)
258258
buffer.write(f"""
259-
self._props['{lit_name}'] = '{lit_val}'
259+
self._props['{lit_name}'] = {lit_val}
260260
self._validators['{lit_name}'] =\
261-
LiteralValidator(plotly_name='{lit_name}', parent_name='{lit_parent}')""")
261+
LiteralValidator(plotly_name='{lit_name}',\
262+
parent_name='{lit_parent}', val={lit_val})""")
262263

263264
buffer.write(f"""
264265

codegen/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ def get_validator_params(self):
423423
if colorscale_node_list:
424424
colorscale_path = colorscale_node_list[0].path_str
425425
params['colorscale_path'] = repr(colorscale_path)
426+
elif self.datatype == 'literal':
427+
params['val'] = self.node_data
426428

427429
return params
428430

plotly/basedatatypes.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,16 @@ def update(self, dict1=None, **kwargs):
378378
for d in [dict1, kwargs]:
379379
if d:
380380
for k, v in d.items():
381-
BaseFigure._perform_update(self[k], v)
381+
if self[k] == ():
382+
# existing data or frames property is empty
383+
# In this case we accept the v as is.
384+
if k == 'data':
385+
self.add_traces(v)
386+
else:
387+
# Accept v
388+
self[k] = v
389+
else:
390+
BaseFigure._perform_update(self[k], v)
382391

383392
return self
384393

@@ -1059,11 +1068,6 @@ def add_traces(self, data, rows=None, cols=None):
10591068
... rows=[1, 2], cols=[1, 1])
10601069
"""
10611070

1062-
if self._in_batch_mode:
1063-
self._batch_layout_edits.clear()
1064-
self._batch_trace_edits.clear()
1065-
raise ValueError('Traces may not be added in a batch context')
1066-
10671071
# Validate traces
10681072
data = self._data_validator.validate_coerce(data)
10691073

@@ -2133,8 +2137,15 @@ def _perform_update(plotly_obj, update_obj):
21332137
BaseFigure._perform_update(
21342138
plotly_obj[key], val)
21352139
elif isinstance(validator, CompoundArrayValidator):
2136-
BaseFigure._perform_update(
2137-
plotly_obj[key], val)
2140+
if plotly_obj[key]:
2141+
# plotly_obj has an existing non-empty array for key
2142+
# In this case we merge val into the existing elements
2143+
BaseFigure._perform_update(
2144+
plotly_obj[key], val)
2145+
else:
2146+
# plotly_obj is an empty or uninitialized list for key
2147+
# In this case we accept val as is
2148+
plotly_obj[key] = val
21382149
else:
21392150
# Assign non-compound value
21402151
plotly_obj[key] = val

plotly/graph_objs/_area.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ def __init__(
785785
from _plotly_utils.basevalidators import LiteralValidator
786786
self._props['type'] = 'area'
787787
self._validators['type'] = LiteralValidator(
788-
plotly_name='type', parent_name='area'
788+
plotly_name='type', parent_name='area', val='area'
789789
)
790790

791791
# Process unknown kwargs

plotly/graph_objs/_bar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2172,7 +2172,7 @@ def __init__(
21722172
from _plotly_utils.basevalidators import LiteralValidator
21732173
self._props['type'] = 'bar'
21742174
self._validators['type'] = LiteralValidator(
2175-
plotly_name='type', parent_name='bar'
2175+
plotly_name='type', parent_name='bar', val='bar'
21762176
)
21772177

21782178
# Process unknown kwargs

plotly/graph_objs/_box.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1574,7 +1574,7 @@ def __init__(
15741574
from _plotly_utils.basevalidators import LiteralValidator
15751575
self._props['type'] = 'box'
15761576
self._validators['type'] = LiteralValidator(
1577-
plotly_name='type', parent_name='box'
1577+
plotly_name='type', parent_name='box', val='box'
15781578
)
15791579

15801580
# Process unknown kwargs

0 commit comments

Comments
 (0)