Skip to content

FIX: Remove type checking for strings in '_validate_linestyle' #8165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -914,24 +914,40 @@ 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*.
# Look first for a valid named line style, like '--' or 'solid'
if isinstance(ls, six.string_types):
try:
return _validate_named_linestyle(ls)
except (UnicodeDecodeError, KeyError):
# On Python 2, string-like *ls*, like for example
# 'solid'.encode('utf-16'), may raise a unicode error.
raise ValueError("the linestyle string {!r} is not a valid "
"string.".format(ls))

if isinstance(ls, (bytes, bytearray)):
# On Python 2, a string-like *ls* should already have lead to a
# successful return or to raising an exception. On Python 3, we have
# to manually raise an exception in the case of a byte-like *ls*.
# Otherwise, if *ls* is of even-length, it will be passed to the
# instance of validate_nseq_float, which will return an absurd on-off
# ink sequence...
raise ValueError("linestyle {!r} neither looks like an on-off ink "
"sequence nor a valid string.".format(ls))

# Look for an on-off ink sequence (in points) *of even length*.
# Offset is set to None.
try:
if len(ls) % 2 != 0:
# Expecting a sequence of even length
raise ValueError
raise ValueError("the linestyle sequence {!r} is not of even "
"length.".format(ls))

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.")
except (ValueError, TypeError):
# TypeError can be raised inside the instance of validate_nseq_float,
# by wrong types passed to float(), like NoneType.
raise ValueError("linestyle {!r} is not a valid on-off ink "
"sequence.".format(ls))


# a map from key -> value, converter
Expand Down
56 changes: 36 additions & 20 deletions lib/matplotlib/tests/test_rcparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,29 +333,45 @@ 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
)
}
)

# The behavior of _validate_linestyle depends on the version of Python.
# ASCII-compliant bytes arguments should pass on Python 2 because of the
# automatic conversion between bytes and strings. Python 3 does not
# perform such a conversion, so the same cases should raise an exception.
#
# Common cases:
ls_test = {'validator': _validate_linestyle,
'success': (('-', '-'), ('solid', 'solid'),
('--', '--'), ('dashed', 'dashed'),
('-.', '-.'), ('dashdot', 'dashdot'),
(':', ':'), ('dotted', 'dotted'),
('', ''), (' ', ' '),
('None', 'none'), ('none', 'none'),
('DoTtEd', 'dotted'), # case-insensitive
(['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
('dotted'.encode('utf-16'), ValueError), # even on PY2
((None, [1, 2]), ValueError), # (offset, dashes) != OK
((0, [1, 2]), ValueError), # idem
((-1, [1, 2]), ValueError), # idem
([1, 2, 3], ValueError), # sequence with odd length
(1.23, ValueError), # not a sequence
)
}
# Add some cases of bytes arguments that Python 2 can convert silently:
ls_bytes_args = (b'dotted', 'dotted'.encode('ascii'))
if six.PY3:
ls_test['fail'] += tuple((arg, ValueError) for arg in ls_bytes_args)
else:
ls_test['success'] += tuple((arg, 'dotted') for arg in ls_bytes_args)
# Update the validation test sequence.
validation_tests += (ls_test,)

for validator_dict in validation_tests:
validator = validator_dict['validator']
if valid:
Expand Down