Repository: plotly/plotly.py
Pull request #1136 (2018-08-29 00:21:07)

Add dash length list to dash property validator
Fix for #1107

This PR introduces a new custom `DashValidator`. This is a `EnumerationValidator` subclass that adds a regular expression to the acceptable values list that covers the dash list case.

It also customizes the validator description so that we don't simple tell users that their specification must match the regular expression `^\d+(\.\d+)?(px|%)?((,|\s)\s*\d+(\.\d+)?(px|%)?)*$` 🙂 

The new `DashValidator` is used for all `dash` properties that support dash lists. At the moment this includes the `dash` properties in all non-WebGL trace types.  The validators for the dash properties in WebGL trace types have not been modified.

cc @APlowman


Patch:
diff --git a/_plotly_utils/basevalidators.py b/_plotly_utils/basevalidators.py
index 864d26725..e7b0d9b09 100644
--- a/_plotly_utils/basevalidators.py
+++ b/_plotly_utils/basevalidators.py
@@ -1677,6 +1677,82 @@ class LiteralValidator(BaseValidator):
             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/codegen/utils.py b/codegen/utils.py
index e7cd3912f..9cde5a78b 100644
--- a/codegen/utils.py
+++ b/codegen/utils.py
@@ -141,6 +141,22 @@ CUSTOM_VALIDATOR_DATATYPES = {
     '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 a5899ed16..13012ce01 100644
--- a/plotly/graph_objs/contour/_line.py
+++ b/plotly/graph_objs/contour/_line.py
@@ -74,11 +74,11 @@ class Line(BaseTraceHierarchyType):
         *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 ad382c5c7..39e2cb07a 100644
--- a/plotly/graph_objs/contourcarpet/_line.py
+++ b/plotly/graph_objs/contourcarpet/_line.py
@@ -74,11 +74,11 @@ class Line(BaseTraceHierarchyType):
         *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 52887f9a9..95a42d9fc 100644
--- a/plotly/graph_objs/histogram2dcontour/_line.py
+++ b/plotly/graph_objs/histogram2dcontour/_line.py
@@ -74,11 +74,11 @@ class Line(BaseTraceHierarchyType):
         *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 62a25b33a..6828610bd 100644
--- a/plotly/graph_objs/ohlc/_line.py
+++ b/plotly/graph_objs/ohlc/_line.py
@@ -16,11 +16,11 @@ class Line(BaseTraceHierarchyType):
         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 6c68f2b5f..0f0ae4f69 100644
--- a/plotly/graph_objs/ohlc/decreasing/_line.py
+++ b/plotly/graph_objs/ohlc/decreasing/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 7e3ee8bb2..0f272f2ca 100644
--- a/plotly/graph_objs/ohlc/increasing/_line.py
+++ b/plotly/graph_objs/ohlc/increasing/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 c0ada4e05..fd5fe29f9 100644
--- a/plotly/graph_objs/scatter/_line.py
+++ b/plotly/graph_objs/scatter/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 8159b7028..bfce0c25c 100644
--- a/plotly/graph_objs/scattercarpet/_line.py
+++ b/plotly/graph_objs/scattercarpet/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 d07a15d14..a955334dc 100644
--- a/plotly/graph_objs/scattergeo/_line.py
+++ b/plotly/graph_objs/scattergeo/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 b07914c48..7653f14f0 100644
--- a/plotly/graph_objs/scatterpolar/_line.py
+++ b/plotly/graph_objs/scatterpolar/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 2b6828b13..91c26f07c 100644
--- a/plotly/graph_objs/scatterternary/_line.py
+++ b/plotly/graph_objs/scatterternary/_line.py
@@ -73,11 +73,11 @@ class Line(BaseTraceHierarchyType):
         *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 c2dbd033f..cad821c37 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 c7b0dd230..676d1ead4 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 34111ebbf..6b192623e 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 e7fe7e185..c2c2d73b3 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 ff0e526ca..f0bf53b1e 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 9744f9c5c..b8d818ec6 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 ad2277356..f422e64d4 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 ff46afea7..14584391e 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 af35be4a6..7097c70fd 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 8e26b4f77..c1f3514fc 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 0f6b67fde..180157b05 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


Test patch:
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 000000000..70ad62058
--- /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)