From 4cb563a222adc5746b42275b6e61d1e85afa2ed3 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Tue, 28 Aug 2018 20:20:23 -0400 Subject: [PATCH] Fix for #1107 --- _plotly_utils/basevalidators.py | 76 +++++++++++++++++++ .../tests/validators/test_dash_validator.py | 53 +++++++++++++ codegen/utils.py | 16 ++++ plotly/graph_objs/contour/_line.py | 10 +-- plotly/graph_objs/contourcarpet/_line.py | 10 +-- plotly/graph_objs/histogram2dcontour/_line.py | 10 +-- plotly/graph_objs/ohlc/_line.py | 10 +-- plotly/graph_objs/ohlc/decreasing/_line.py | 10 +-- plotly/graph_objs/ohlc/increasing/_line.py | 10 +-- plotly/graph_objs/scatter/_line.py | 10 +-- plotly/graph_objs/scattercarpet/_line.py | 10 +-- plotly/graph_objs/scattergeo/_line.py | 10 +-- plotly/graph_objs/scatterpolar/_line.py | 10 +-- plotly/graph_objs/scatterternary/_line.py | 10 +-- plotly/validators/contour/line/_dash.py | 2 +- plotly/validators/contourcarpet/line/_dash.py | 2 +- .../histogram2dcontour/line/_dash.py | 2 +- .../validators/ohlc/decreasing/line/_dash.py | 2 +- .../validators/ohlc/increasing/line/_dash.py | 2 +- plotly/validators/ohlc/line/_dash.py | 2 +- plotly/validators/scatter/line/_dash.py | 2 +- plotly/validators/scattercarpet/line/_dash.py | 2 +- plotly/validators/scattergeo/line/_dash.py | 2 +- plotly/validators/scatterpolar/line/_dash.py | 2 +- .../validators/scatterternary/line/_dash.py | 2 +- 25 files changed, 211 insertions(+), 66 deletions(-) create mode 100644 _plotly_utils/tests/validators/test_dash_validator.py diff --git a/_plotly_utils/basevalidators.py b/_plotly_utils/basevalidators.py index 864d2672536..e7b0d9b093f 100644 --- a/_plotly_utils/basevalidators.py +++ b/_plotly_utils/basevalidators.py @@ -1677,6 +1677,82 @@ def validate_coerce(self, v): return v +class DashValidator(EnumeratedValidator): + """ + Special case validator for handling dash properties that may be specified + as lists of dash lengths. These are not currently specified in the + schema. + + "dash": { + "valType": "string", + "values": [ + "solid", + "dot", + "dash", + "longdash", + "dashdot", + "longdashdot" + ], + "dflt": "solid", + "role": "style", + "editType": "style", + "description": "Sets the dash style of lines. Set to a dash type + string (*solid*, *dot*, *dash*, *longdash*, *dashdot*, or + *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*)." + }, + """ + def __init__(self, + plotly_name, + parent_name, + values, + **kwargs): + + # Add regex to handle dash length lists + dash_list_regex = \ + r"/^\d+(\.\d+)?(px|%)?((,|\s)\s*\d+(\.\d+)?(px|%)?)*$/" + + values = values + [dash_list_regex] + + # Call EnumeratedValidator superclass + super(DashValidator, self).__init__( + plotly_name=plotly_name, + parent_name=parent_name, + values=values, **kwargs) + + def description(self): + + # Separate regular values from regular expressions + enum_vals = [] + enum_regexs = [] + for v, regex in zip(self.values, self.val_regexs): + if regex is not None: + enum_regexs.append(regex.pattern) + else: + enum_vals.append(v) + desc = ("""\ + The '{name}' property is an enumeration that may be specified as:""" + .format(name=self.plotly_name)) + + if enum_vals: + enum_vals_str = '\n'.join( + textwrap.wrap( + repr(enum_vals), + initial_indent=' ' * 12, + subsequent_indent=' ' * 12, + break_on_hyphens=False, + width=80)) + + desc = desc + """ + - One of the following dash styles: +{enum_vals_str}""".format(enum_vals_str=enum_vals_str) + + desc = desc + """ + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) +""" + return desc + + class ImageUriValidator(BaseValidator): _PIL = None diff --git a/_plotly_utils/tests/validators/test_dash_validator.py b/_plotly_utils/tests/validators/test_dash_validator.py new file mode 100644 index 00000000000..70ad62058e0 --- /dev/null +++ b/_plotly_utils/tests/validators/test_dash_validator.py @@ -0,0 +1,53 @@ +import pytest +from _plotly_utils.basevalidators import DashValidator + + +# Constants +# --------- +dash_types = ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + + +# Fixtures +# -------- +@pytest.fixture() +def validator(): + return DashValidator('prop', 'parent', dash_types) + + +# Acceptance +# ---------- +@pytest.mark.parametrize('val', + dash_types) +def test_acceptance_dash_types(val, validator): + # Values should be accepted and returned unchanged + assert validator.validate_coerce(val) == val + + +@pytest.mark.parametrize('val', + ['2', '2.2', '2.002', '1 2 002', + '1,2,3', '1, 2, 3', + '1px 2px 3px', '1.5px, 2px, 3.9px', + '23% 18% 13px', '200% 3px']) +def test_acceptance_dash_lists(val, validator): + # Values should be accepted and returned unchanged + assert validator.validate_coerce(val) == val + + +# Rejection +# --------- +# ### Value Rejection ### +@pytest.mark.parametrize('val', ['bogus', 'not-a-dash']) +def test_rejection_by_bad_dash_type(val, validator): + with pytest.raises(ValueError) as validation_failure: + validator.validate_coerce(val) + + assert 'Invalid value' in str(validation_failure.value) + + +@pytest.mark.parametrize('val', + ['', '1,,3,4', '2 3 C', '2pxx 3 4']) +def test_rejection_by_bad_dash_list(val, validator): + with pytest.raises(ValueError) as validation_failure: + validator.validate_coerce(val) + + assert 'Invalid value' in str(validation_failure.value) diff --git a/codegen/utils.py b/codegen/utils.py index e7cd3912fa2..9cde5a78b3f 100644 --- a/codegen/utils.py +++ b/codegen/utils.py @@ -141,6 +141,22 @@ def write_init_py(pkg_root, path_parts, import_pairs): 'frame.layout': 'plotly.validators.LayoutValidator' } +# Add custom dash validators +CUSTOM_VALIDATOR_DATATYPES.update( + {prop: '_plotly_utils.basevalidators.DashValidator' + for prop in [ + 'scatter.line.dash', + 'histogram2dcontour.line.dash', + 'scattergeo.line.dash', + 'scatterpolar.line.dash', + 'ohlc.line.dash', + 'ohlc.decreasing.line.dash', + 'ohlc.increasing.line.dash', + 'contourcarpet.line.dash', + 'contour.line.dash', + 'scatterternary.line.dash', + 'scattercarpet.line.dash']}) + # Mapping from property string (as found in plot-schema.json) to a custom # class name. If not included here, names are converted to TitleCase and # underscores are removed. diff --git a/plotly/graph_objs/contour/_line.py b/plotly/graph_objs/contour/_line.py index a5899ed16d3..13012ce0158 100644 --- a/plotly/graph_objs/contour/_line.py +++ b/plotly/graph_objs/contour/_line.py @@ -74,11 +74,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/contourcarpet/_line.py b/plotly/graph_objs/contourcarpet/_line.py index ad382c5c70c..39e2cb07a25 100644 --- a/plotly/graph_objs/contourcarpet/_line.py +++ b/plotly/graph_objs/contourcarpet/_line.py @@ -74,11 +74,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/histogram2dcontour/_line.py b/plotly/graph_objs/histogram2dcontour/_line.py index 52887f9a94a..95a42d9fccc 100644 --- a/plotly/graph_objs/histogram2dcontour/_line.py +++ b/plotly/graph_objs/histogram2dcontour/_line.py @@ -74,11 +74,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/ohlc/_line.py b/plotly/graph_objs/ohlc/_line.py index 62a25b33af2..6828610bd01 100644 --- a/plotly/graph_objs/ohlc/_line.py +++ b/plotly/graph_objs/ohlc/_line.py @@ -16,11 +16,11 @@ def dash(self): set per direction via `increasing.line.dash` and `decreasing.line.dash`. - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/ohlc/decreasing/_line.py b/plotly/graph_objs/ohlc/decreasing/_line.py index 6c68f2b5f42..0f0ae4f69c0 100644 --- a/plotly/graph_objs/ohlc/decreasing/_line.py +++ b/plotly/graph_objs/ohlc/decreasing/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/ohlc/increasing/_line.py b/plotly/graph_objs/ohlc/increasing/_line.py index 7e3ee8bb2b0..0f272f2ca22 100644 --- a/plotly/graph_objs/ohlc/increasing/_line.py +++ b/plotly/graph_objs/ohlc/increasing/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/scatter/_line.py b/plotly/graph_objs/scatter/_line.py index c0ada4e0594..fd5fe29f924 100644 --- a/plotly/graph_objs/scatter/_line.py +++ b/plotly/graph_objs/scatter/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/scattercarpet/_line.py b/plotly/graph_objs/scattercarpet/_line.py index 8159b702889..bfce0c25c5f 100644 --- a/plotly/graph_objs/scattercarpet/_line.py +++ b/plotly/graph_objs/scattercarpet/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/scattergeo/_line.py b/plotly/graph_objs/scattergeo/_line.py index d07a15d14ce..a955334dcbf 100644 --- a/plotly/graph_objs/scattergeo/_line.py +++ b/plotly/graph_objs/scattergeo/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/scatterpolar/_line.py b/plotly/graph_objs/scatterpolar/_line.py index b07914c4819..7653f14f05c 100644 --- a/plotly/graph_objs/scatterpolar/_line.py +++ b/plotly/graph_objs/scatterpolar/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/graph_objs/scatterternary/_line.py b/plotly/graph_objs/scatterternary/_line.py index 2b6828b130f..91c26f07cb3 100644 --- a/plotly/graph_objs/scatterternary/_line.py +++ b/plotly/graph_objs/scatterternary/_line.py @@ -73,11 +73,11 @@ def dash(self): *longdashdot*) or a dash length list in px (eg *5px,10px,2px,2px*). - The 'dash' property is a string and must be specified as: - - One of the following strings: - ['solid', 'dot', 'dash', 'longdash', 'dashdot', - 'longdashdot'] - - A number that will be converted to a string + The 'dash' property is an enumeration that may be specified as: + - One of the following dash styles: + ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'] + - A string containing a dash length list in pixels or percentages + (e.g. '5px 10px 2px 2px', '5, 10, 2, 2', '10% 20% 40%', etc.) Returns ------- diff --git a/plotly/validators/contour/line/_dash.py b/plotly/validators/contour/line/_dash.py index c2dbd033f57..cad821c3736 100644 --- a/plotly/validators/contour/line/_dash.py +++ b/plotly/validators/contour/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='contour.line', **kwargs diff --git a/plotly/validators/contourcarpet/line/_dash.py b/plotly/validators/contourcarpet/line/_dash.py index c7b0dd23067..676d1ead4ee 100644 --- a/plotly/validators/contourcarpet/line/_dash.py +++ b/plotly/validators/contourcarpet/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='contourcarpet.line', **kwargs diff --git a/plotly/validators/histogram2dcontour/line/_dash.py b/plotly/validators/histogram2dcontour/line/_dash.py index 34111ebbf59..6b192623e51 100644 --- a/plotly/validators/histogram2dcontour/line/_dash.py +++ b/plotly/validators/histogram2dcontour/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, diff --git a/plotly/validators/ohlc/decreasing/line/_dash.py b/plotly/validators/ohlc/decreasing/line/_dash.py index e7fe7e185df..c2c2d73b34e 100644 --- a/plotly/validators/ohlc/decreasing/line/_dash.py +++ b/plotly/validators/ohlc/decreasing/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='ohlc.decreasing.line', **kwargs diff --git a/plotly/validators/ohlc/increasing/line/_dash.py b/plotly/validators/ohlc/increasing/line/_dash.py index ff0e526cac2..f0bf53b1e24 100644 --- a/plotly/validators/ohlc/increasing/line/_dash.py +++ b/plotly/validators/ohlc/increasing/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='ohlc.increasing.line', **kwargs diff --git a/plotly/validators/ohlc/line/_dash.py b/plotly/validators/ohlc/line/_dash.py index 9744f9c5c84..b8d818ec6e9 100644 --- a/plotly/validators/ohlc/line/_dash.py +++ b/plotly/validators/ohlc/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__(self, plotly_name='dash', parent_name='ohlc.line', **kwargs): super(DashValidator, self).__init__( diff --git a/plotly/validators/scatter/line/_dash.py b/plotly/validators/scatter/line/_dash.py index ad2277356b3..f422e64d459 100644 --- a/plotly/validators/scatter/line/_dash.py +++ b/plotly/validators/scatter/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='scatter.line', **kwargs diff --git a/plotly/validators/scattercarpet/line/_dash.py b/plotly/validators/scattercarpet/line/_dash.py index ff46afea79b..14584391e1d 100644 --- a/plotly/validators/scattercarpet/line/_dash.py +++ b/plotly/validators/scattercarpet/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='scattercarpet.line', **kwargs diff --git a/plotly/validators/scattergeo/line/_dash.py b/plotly/validators/scattergeo/line/_dash.py index af35be4a6a1..7097c70fd7e 100644 --- a/plotly/validators/scattergeo/line/_dash.py +++ b/plotly/validators/scattergeo/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='scattergeo.line', **kwargs diff --git a/plotly/validators/scatterpolar/line/_dash.py b/plotly/validators/scatterpolar/line/_dash.py index 8e26b4f7766..c1f3514fc3b 100644 --- a/plotly/validators/scatterpolar/line/_dash.py +++ b/plotly/validators/scatterpolar/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='scatterpolar.line', **kwargs diff --git a/plotly/validators/scatterternary/line/_dash.py b/plotly/validators/scatterternary/line/_dash.py index 0f6b67fde39..180157b050f 100644 --- a/plotly/validators/scatterternary/line/_dash.py +++ b/plotly/validators/scatterternary/line/_dash.py @@ -1,7 +1,7 @@ import _plotly_utils.basevalidators -class DashValidator(_plotly_utils.basevalidators.StringValidator): +class DashValidator(_plotly_utils.basevalidators.DashValidator): def __init__( self, plotly_name='dash', parent_name='scatterternary.line', **kwargs