ENH: Stricter validation of line style rcParams (and extended accepted types for `grid.linestyle`) #8040

Merged
merged 15 commits into from Feb 18, 2017
+111 −19
Split
@@ -0,0 +1,31 @@
+Validation of line style rcParams
+---------------------------------
+
+Stricter validation
+```````````````````
+The validation of rcParams that are related to line styles
+(``lines.linestyle``, ``boxplot.*.linestyle``, ``grid.linestyle`` and
+``contour.negative_linestyle``) now effectively checks that the values
+are valid line styles. Strings like ``dashed`` or ``--`` are accepted,
+as well as even-length sequences of on-off ink like ``[1, 1.65]``. In
+this latter case, the offset value is handled internally and should *not*
+be provided by the user.
+
+The validation is case-insensitive.
+
+Deprecation of the former validators for ``contour.negative_linestyle``
+```````````````````````````````````````````````````````````````````````
+The new validation scheme replaces the former one used for the
+``contour.negative_linestyle`` rcParams, that was limited to ``solid``
+and ``dashed`` line styles.
+
+The former public validation functions ``validate_negative_linestyle``
+and ``validate_negative_linestyle_legacy`` will be deprecated in 2.1 and
+may be removed in 2.3. There are no public functions to replace them.
+
+Examples of use
+```````````````
+::
+
+ grid.linestyle : (1, 3) # loosely dotted grid lines
+ contour.negative_linestyle : dashdot # previously only solid or dashed
View
@@ -25,7 +25,7 @@
import warnings
import re
-from matplotlib.cbook import mplDeprecation
+from matplotlib.cbook import mplDeprecation, deprecated, ls_mapper
from matplotlib.fontconfig_pattern import parse_fontconfig_pattern
from matplotlib.colors import is_color_like
@@ -530,20 +530,28 @@ def validate_ps_distiller(s):
'top', 'none'])
validate_fillstylelist = _listify_validator(validate_fillstyle)
-validate_negative_linestyle = ValidateInStrings('negative_linestyle',
- ['solid', 'dashed'],
- ignorecase=True)
+_validate_negative_linestyle = ValidateInStrings('negative_linestyle',
+ ['solid', 'dashed'],
+ ignorecase=True)
+@deprecated('2.1',
+ addendum=(" See 'validate_negative_linestyle_legacy' " +
+ "deprecation warning for more information."))
+def validate_negative_linestyle(s):
+ return _validate_negative_linestyle(s)
+
+
+@deprecated('2.1',
+ addendum=(" The 'contour.negative_linestyle' rcParam now " +
+ "follows the same validation as the other rcParams " +
+ "that are related to line style."))
def validate_negative_linestyle_legacy(s):
try:
res = validate_negative_linestyle(s)
return res
except ValueError:
dashes = validate_nseq_float(2)(s)
- warnings.warn("Deprecated negative_linestyle specification; use "
- "'solid' or 'dashed'",
- mplDeprecation)
return (0, dashes) # (offset, (solid, blank))
@@ -888,6 +896,39 @@ def validate_animation_writer_path(p):
modules["matplotlib.animation"].writers.set_dirty()
return p
+# A validator dedicated to the named line styles, based on the items in
+# ls_mapper, and a list of possible strings read from Line2D.set_linestyle
+_validate_named_linestyle = ValidateInStrings('linestyle',
+ list(six.iterkeys(ls_mapper)) +
+ list(six.itervalues(ls_mapper)) +
+ ['None', 'none', ' ', ''],
+ ignorecase=True)
+
+
+def _validate_linestyle(ls):
+ """
+ A validator for all possible line styles, the named ones *and*
+ the on-off ink sequences.
+ """
+ # Named line style, like u'--' or u'solid'
+ if isinstance(ls, six.text_type):
+ return _validate_named_linestyle(ls)
+
+ # On-off ink (in points) sequence *of even length*.
+ # Offset is set to None.
+ try:
+ if len(ls) % 2 != 0:
+ # Expecting a sequence of even length
+ raise ValueError
+ return (None, validate_nseq_float()(ls))
+ except (ValueError, TypeError):
+ # TypeError can be raised by wrong types passed to float()
+ # (called inside the instance of validate_nseq_float).
+ pass
+
+ raise ValueError("linestyle must be a string or " +
+ "an even-length sequence of floats.")
+
# a map from key -> value, converter
defaultParams = {
@@ -912,7 +953,7 @@ def validate_animation_writer_path(p):
# line props
'lines.linewidth': [1.5, validate_float], # line width in points
- 'lines.linestyle': ['-', six.text_type], # solid line
+ 'lines.linestyle': ['-', _validate_linestyle], # solid line
'lines.color': ['C0', validate_color], # first color in color cycle
'lines.marker': ['None', six.text_type], # marker name
'lines.markeredgewidth': [1.0, validate_float],
@@ -961,31 +1002,31 @@ def validate_animation_writer_path(p):
'boxplot.flierprops.markerfacecolor': ['none', validate_color_or_auto],
'boxplot.flierprops.markeredgecolor': ['k', validate_color],
'boxplot.flierprops.markersize': [6, validate_float],
- 'boxplot.flierprops.linestyle': ['none', six.text_type],
+ 'boxplot.flierprops.linestyle': ['none', _validate_linestyle],
'boxplot.flierprops.linewidth': [1.0, validate_float],
'boxplot.boxprops.color': ['k', validate_color],
'boxplot.boxprops.linewidth': [1.0, validate_float],
- 'boxplot.boxprops.linestyle': ['-', six.text_type],
+ 'boxplot.boxprops.linestyle': ['-', _validate_linestyle],
'boxplot.whiskerprops.color': ['k', validate_color],
'boxplot.whiskerprops.linewidth': [1.0, validate_float],
- 'boxplot.whiskerprops.linestyle': ['-', six.text_type],
+ 'boxplot.whiskerprops.linestyle': ['-', _validate_linestyle],
'boxplot.capprops.color': ['k', validate_color],
'boxplot.capprops.linewidth': [1.0, validate_float],
- 'boxplot.capprops.linestyle': ['-', six.text_type],
+ 'boxplot.capprops.linestyle': ['-', _validate_linestyle],
'boxplot.medianprops.color': ['C1', validate_color],
'boxplot.medianprops.linewidth': [1.0, validate_float],
- 'boxplot.medianprops.linestyle': ['-', six.text_type],
+ 'boxplot.medianprops.linestyle': ['-', _validate_linestyle],
'boxplot.meanprops.color': ['C2', validate_color],
'boxplot.meanprops.marker': ['^', six.text_type],
'boxplot.meanprops.markerfacecolor': ['C2', validate_color],
'boxplot.meanprops.markeredgecolor': ['C2', validate_color],
'boxplot.meanprops.markersize': [6, validate_float],
- 'boxplot.meanprops.linestyle': ['--', six.text_type],
+ 'boxplot.meanprops.linestyle': ['--', _validate_linestyle],
'boxplot.meanprops.linewidth': [1.0, validate_float],
## font props
@@ -1051,8 +1092,7 @@ def validate_animation_writer_path(p):
'image.composite_image': [True, validate_bool],
# contour props
- 'contour.negative_linestyle': ['dashed',
- validate_negative_linestyle_legacy],
+ 'contour.negative_linestyle': ['dashed', _validate_linestyle],
'contour.corner_mask': [True, validate_corner_mask],
# errorbar props
@@ -1215,7 +1255,7 @@ def validate_animation_writer_path(p):
'ytick.direction': ['out', six.text_type], # direction of yticks
'grid.color': ['#b0b0b0', validate_color], # grid color
- 'grid.linestyle': ['-', six.text_type], # solid
+ 'grid.linestyle': ['-', _validate_linestyle], # solid
'grid.linewidth': [0.8, validate_float], # in points
'grid.alpha': [1.0, validate_float],
@@ -29,7 +29,8 @@
validate_nseq_float,
validate_cycler,
validate_hatch,
- validate_hist_bins)
+ validate_hist_bins,
+ _validate_linestyle)
mpl.rc('text', usetex=False)
@@ -333,6 +334,26 @@ def generate_validator_testcases(valid):
),
'fail': (('aardvark', ValueError),
)
+ },
+ {'validator': _validate_linestyle, # NB: case-insensitive
+ 'success': (('-', '-'), ('solid', 'solid'),
+ ('--', '--'), ('dashed', 'dashed'),
+ ('-.', '-.'), ('dashdot', 'dashdot'),
+ (':', ':'), ('dotted', 'dotted'),
+ ('', ''), (' ', ' '),
+ ('None', 'none'), ('none', 'none'),
+ ('DoTtEd', 'dotted'),
+ (['1.23', '4.56'], (None, [1.23, 4.56])),
+ ([1.23, 456], (None, [1.23, 456.0])),
+ ([1, 2, 3, 4], (None, [1.0, 2.0, 3.0, 4.0])),
+ ),
+ 'fail': (('aardvark', ValueError), # not a valid string
+ ((None, [1, 2]), ValueError), # (offset, dashes) is not OK
+ ((0, [1, 2]), ValueError), # idem
+ ((-1, [1, 2]), ValueError), # idem
+ ([1, 2, 3], ValueError), # not a sequence of even length
+ (1.23, ValueError) # not a sequence
+ )
}
)
View
@@ -471,7 +471,7 @@ backend : $TEMPLATE_BACKEND
# such as a PDF.
### CONTOUR PLOTS
-#contour.negative_linestyle : dashed # dashed | solid
+#contour.negative_linestyle : dashed # string or on-off ink sequence
#contour.corner_mask : True # True | False | legacy
### ERRORBAR PLOTS