Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 595868a

Browse files
committed
Merge pull request #5452 from u55/cycler_generic_iterator
Fix axes.set_prop_cycle to handle any generic iterable sequence.
2 parents 838502e + 3571921 commit 595868a

File tree

3 files changed

+83
-7
lines changed

3 files changed

+83
-7
lines changed

lib/matplotlib/rcsetup.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@
2222
import operator
2323
import os
2424
import warnings
25+
try:
26+
import collections.abc as abc
27+
except ImportError:
28+
# python 2
29+
import collections as abc
2530
from matplotlib.fontconfig_pattern import parse_fontconfig_pattern
2631
from matplotlib.colors import is_color_like
2732

@@ -78,15 +83,19 @@ def f(s):
7883
return [scalar_validator(v.strip()) for v in s if v.strip()]
7984
else:
8085
raise
81-
elif type(s) in (list, tuple):
86+
# We should allow any generic sequence type, including generators,
87+
# Numpy ndarrays, and pandas data structures. However, unordered
88+
# sequences, such as sets, should be allowed but discouraged unless the
89+
# user desires pseudorandom behavior.
90+
elif isinstance(s, abc.Iterable) and not isinstance(s, abc.Mapping):
8291
# The condition on this list comprehension will preserve the
8392
# behavior of filtering out any empty strings (behavior was
8493
# from the original validate_stringlist()), while allowing
8594
# any non-string/text scalar values such as numbers and arrays.
8695
return [scalar_validator(v) for v in s
8796
if not isinstance(v, six.string_types) or v]
8897
else:
89-
msg = "'s' must be of type [ string | list | tuple ]"
98+
msg = "{0!r} must be of type: string or non-dictionary iterable.".format(s)
9099
raise ValueError(msg)
91100
f.__doc__ = scalar_validator.__doc__
92101
return f

lib/matplotlib/tests/test_cycles.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
from matplotlib.testing.decorators import image_comparison
1+
from matplotlib.testing.decorators import image_comparison, cleanup
22
import matplotlib.pyplot as plt
33
import numpy as np
4+
from nose.tools import assert_raises
45

56
from cycler import cycler
67

@@ -42,6 +43,27 @@ def test_marker_cycle():
4243
ax.legend(loc='upper left')
4344

4445

46+
# Reuse the image from test_marker_cycle()
47+
@image_comparison(baseline_images=['marker_cycle'], remove_text=True,
48+
extensions=['png'])
49+
def test_marker_cycle_keywords():
50+
fig = plt.figure()
51+
ax = fig.add_subplot(111)
52+
# Test keyword arguments, numpy arrays, and generic iterators
53+
ax.set_prop_cycle(color=np.array(['r', 'g', 'y']),
54+
marker=iter(['.', '*', 'x']))
55+
xs = np.arange(10)
56+
ys = 0.25 * xs + 2
57+
ax.plot(xs, ys, label='red dot', lw=4, ms=16)
58+
ys = 0.45 * xs + 3
59+
ax.plot(xs, ys, label='green star', lw=4, ms=16)
60+
ys = 0.65 * xs + 4
61+
ax.plot(xs, ys, label='yellow x', lw=4, ms=16)
62+
ys = 0.85 * xs + 5
63+
ax.plot(xs, ys, label='red2 dot', lw=4, ms=16)
64+
ax.legend(loc='upper left')
65+
66+
4567
@image_comparison(baseline_images=['lineprop_cycle_basic'], remove_text=True,
4668
extensions=['png'])
4769
def test_linestylecycle_basic():
@@ -104,6 +126,44 @@ def test_fillcycle_ignore():
104126
ax.legend(loc='upper left')
105127

106128

129+
@cleanup
130+
def test_valid_input_forms():
131+
fig, ax = plt.subplots()
132+
# These should not raise an error.
133+
ax.set_prop_cycle(None)
134+
ax.set_prop_cycle(cycler('linewidth', [1, 2]))
135+
ax.set_prop_cycle('color', 'rgywkbcm')
136+
ax.set_prop_cycle('linewidth', (1, 2))
137+
ax.set_prop_cycle('linewidth', [1, 2])
138+
ax.set_prop_cycle('linewidth', iter([1, 2]))
139+
ax.set_prop_cycle('linewidth', np.array([1, 2]))
140+
ax.set_prop_cycle('color', np.array([[1, 0, 0],
141+
[0, 1, 0],
142+
[0, 0, 1]]))
143+
ax.set_prop_cycle(lw=[1, 2], color=['k', 'w'], ls=['-', '--'])
144+
ax.set_prop_cycle(lw=np.array([1, 2]),
145+
color=np.array(['k', 'w']),
146+
ls=np.array(['-', '--']))
147+
assert True
148+
149+
150+
@cleanup
151+
def test_invalid_input_forms():
152+
fig, ax = plt.subplots()
153+
with assert_raises((TypeError, ValueError)):
154+
ax.set_prop_cycle(1)
155+
with assert_raises((TypeError, ValueError)):
156+
ax.set_prop_cycle([1, 2])
157+
with assert_raises((TypeError, ValueError)):
158+
ax.set_prop_cycle('color', 'fish')
159+
with assert_raises((TypeError, ValueError)):
160+
ax.set_prop_cycle('linewidth', 1)
161+
with assert_raises((TypeError, ValueError)):
162+
ax.set_prop_cycle('linewidth', {'1': 1, '2': 2})
163+
with assert_raises((TypeError, ValueError)):
164+
ax.set_prop_cycle(linewidth=1, color='r')
165+
166+
107167
if __name__ == '__main__':
108168
import nose
109169
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

lib/matplotlib/tests/test_rcparams.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,10 +280,15 @@ def test_validators():
280280
('aardvark, ,', ['aardvark']),
281281
(['a', 'b'], ['a', 'b']),
282282
(('a', 'b'), ['a', 'b']),
283-
((1, 2), ['1', '2'])),
284-
'fail': ((dict(), ValueError),
285-
(1, ValueError),)
286-
},
283+
(iter(['a', 'b']), ['a', 'b']),
284+
(np.array(['a', 'b']), ['a', 'b']),
285+
((1, 2), ['1', '2']),
286+
(np.array([1, 2]), ['1', '2']),
287+
),
288+
'fail': ((dict(), ValueError),
289+
(1, ValueError),
290+
)
291+
},
287292
{'validator': validate_nseq_int(2),
288293
'success': ((_, [1, 2])
289294
for _ in ('1, 2', [1.5, 2.5], [1, 2],
@@ -353,6 +358,8 @@ def test_validators():
353358
(['', 'g', 'blue'], ['g', 'blue']),
354359
([np.array([1, 0, 0]), np.array([0, 1, 0])],
355360
np.array([[1, 0, 0], [0, 1, 0]])),
361+
(np.array([[1, 0, 0], [0, 1, 0]]),
362+
np.array([[1, 0, 0], [0, 1, 0]])),
356363
),
357364
'fail': (('fish', ValueError),
358365
),

0 commit comments

Comments
 (0)