Skip to content

Commit d9b8020

Browse files
committed
FIX: Error-handling for from_list inputs
Assume that the 'colors' argument is always a list of colors. If 'to_rgba_array' fails, attempt to unpack the values as a list of '(color, value)' tuples. Fixes matplotlib#29042. Passing '(color, alpha)' tuples are nor correctly parsed as a list of colors. Check for range and monotony of values when unpacking (value, color) pairs. Implemented tests for different combinations of inputs.
1 parent b358c98 commit d9b8020

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

lib/matplotlib/colors.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"""
4141

4242
import base64
43-
from collections.abc import Sized, Sequence, Mapping
43+
from collections.abc import Sequence, Mapping
4444
import functools
4545
import importlib
4646
import inspect
@@ -1091,22 +1091,35 @@ def from_list(name, colors, N=256, gamma=1.0):
10911091
range :math:`[0, 1]`; i.e. 0 maps to ``colors[0]`` and 1 maps to
10921092
``colors[-1]``.
10931093
If (value, color) pairs are given, the mapping is from *value*
1094-
to *color*. This can be used to divide the range unevenly.
1094+
to *color*. This can be used to divide the range unevenly. The
1095+
values must increase monotonically from 0 to 1.
10951096
N : int
10961097
The number of RGB quantization levels.
10971098
gamma : float
10981099
"""
10991100
if not np.iterable(colors):
11001101
raise ValueError('colors must be iterable')
11011102

1102-
if (isinstance(colors[0], Sized) and len(colors[0]) == 2
1103-
and not isinstance(colors[0], str)):
1104-
# List of value, color pairs
1105-
vals, colors = zip(*colors)
1106-
else:
1103+
try:
1104+
# Assume the passed colors are a list of colors
1105+
# and not a (value, color) tuple.
1106+
r, g, b, a = to_rgba_array(colors).T
11071107
vals = np.linspace(0, 1, len(colors))
1108+
except Exception as e:
1109+
# Assume the passed values are a list of
1110+
# (value, color) tuples.
1111+
try:
1112+
_vals, _colors = itertools.zip_longest(*colors)
1113+
except Exception as e2:
1114+
raise e2 from e
1115+
vals = np.asarray(_vals)
1116+
if np.min(vals) < 0 or np.max(vals) > 1 or np.any(np.diff(vals) <= 0):
1117+
raise ValueError(
1118+
"the values passed in the (value, color) pairs "
1119+
"must increase monotonically from 0 to 1."
1120+
)
1121+
r, g, b, a = to_rgba_array(_colors).T
11081122

1109-
r, g, b, a = to_rgba_array(colors).T
11101123
cdict = {
11111124
"red": np.column_stack([vals, r, r]),
11121125
"green": np.column_stack([vals, g, g]),

lib/matplotlib/tests/test_colors.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,3 +1734,34 @@ def test_colorizer_vmin_vmax():
17341734
assert ca.vmax == 3.0
17351735
assert ca.norm.vmin == 1.0
17361736
assert ca.norm.vmax == 3.0
1737+
1738+
1739+
def test_LinearSegmentedColormap_from_list_color_alpha_tuple():
1740+
"""
1741+
GitHub issue #29042: A bug in 'from_list' causes an error
1742+
when passing a tuple (str, float) where the string is a
1743+
color name or grayscale value and float is an alpha value.
1744+
"""
1745+
colors = [("red", 0.3), ("0.42", 0.1), "green"]
1746+
cmap = mcolors.LinearSegmentedColormap.from_list("lsc", colors, N=3)
1747+
assert_array_almost_equal(cmap([.0, 0.5, 1.]), to_rgba_array(colors))
1748+
1749+
1750+
@pytest.mark.parametrize("colors",
1751+
[[(0.42, "blue"), (.1, .1, .1, .1)],
1752+
["blue", (0.42, "red")],
1753+
["blue", (.1, .1, .1, .1), ("red", 2)],
1754+
[(0, "red"), (1.1, "blue")],
1755+
[(0.52, "red"), (0.42, "blue")]])
1756+
def test_LinearSegmentedColormap_from_list_invalid_inputs(colors):
1757+
with pytest.raises(ValueError):
1758+
mcolors.LinearSegmentedColormap.from_list("lsc", colors)
1759+
1760+
1761+
def test_LinearSegmentedColormap_from_list_value_color_tuple():
1762+
value_color_tuples = [(0, "red"), (0.6, "blue"), (1, "green")]
1763+
cmap = mcolors.LinearSegmentedColormap.from_list("lsc", value_color_tuples, N=11)
1764+
assert_array_almost_equal(
1765+
cmap([value for value, _ in value_color_tuples]),
1766+
to_rgba_array([color for _, color in value_color_tuples]),
1767+
)

0 commit comments

Comments
 (0)