Skip to content

Commit 4ab6289

Browse files
authored
Fix ListedColormap N deprecation (#769)
Preparing for future matplotlib releases
1 parent 9175faf commit 4ab6289

2 files changed

Lines changed: 94 additions & 2 deletions

File tree

ultraplot/colors.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# colors or truncate colors. So we translate the relevant ListedColormaps to
1818
# LinearSegmentedColormaps for consistency. See :rc:`cmap.listedthresh`
1919
import functools
20+
import itertools
2021
import json
2122
import os
2223
import re
@@ -1715,6 +1716,20 @@ def __repr__(self):
17151716
string += "})"
17161717
return string
17171718

1719+
@property
1720+
def monochrome(self):
1721+
"""Whether every color is identical, normalized to a Python boolean."""
1722+
if hasattr(self, "_monochrome"):
1723+
return self._monochrome
1724+
try:
1725+
return bool(super().monochrome)
1726+
except AttributeError:
1727+
return False
1728+
1729+
@monochrome.setter
1730+
def monochrome(self, value):
1731+
self._monochrome = bool(value)
1732+
17181733
def __init__(self, colors, name=None, N=None, alpha=None, **kwargs):
17191734
"""
17201735
Parameters
@@ -1748,7 +1763,13 @@ def __init__(self, colors, name=None, N=None, alpha=None, **kwargs):
17481763
# identical monochromatic ListedColormaps when it receives scalar colors.
17491764
N = _not_none(N, len(colors))
17501765
name = _not_none(name, DEFAULT_NAME)
1751-
super().__init__(colors, name=name, N=N, **kwargs)
1766+
if isinstance(colors, str):
1767+
colors = [colors] * N
1768+
elif np.iterable(colors):
1769+
colors = list(itertools.islice(itertools.cycle(colors), N))
1770+
else:
1771+
colors = [colors] * N
1772+
super().__init__(colors, name=name, **kwargs)
17521773
if alpha is not None:
17531774
self.set_alpha(alpha)
17541775
for i, color in enumerate(self.colors):

ultraplot/tests/test_colors.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,83 @@
11
import os
2+
import warnings
3+
4+
import matplotlib as mpl
5+
import matplotlib.colors as mcolors
26
import pytest
37
import numpy as np
4-
import matplotlib.colors as mcolors
58

69
from ultraplot import colors as pcolors
710
from ultraplot import config
811

912

13+
@pytest.mark.parametrize(
14+
("N", "expected"),
15+
(
16+
(None, ["#ff0000", "#0000ff"]),
17+
(0, []),
18+
(1, ["#ff0000"]),
19+
(3, ["#ff0000", "#0000ff", "#ff0000"]),
20+
),
21+
)
22+
def test_discrete_colormap_resizes_without_listed_n_warning(N, expected):
23+
"""DiscreteColormap preserves N semantics without deprecated mpl input."""
24+
with warnings.catch_warnings():
25+
warnings.filterwarnings(
26+
"error",
27+
message=r"Passing 'N' to ListedColormap.*",
28+
category=mpl.MatplotlibDeprecationWarning,
29+
)
30+
cmap = pcolors.DiscreteColormap(["red", "blue"], N=N)
31+
32+
assert cmap.N == len(expected)
33+
assert [mcolors.to_hex(color) for color in cmap.colors] == expected
34+
35+
36+
@pytest.mark.parametrize(
37+
("N", "expected_size"),
38+
(
39+
(None, 3),
40+
(1, 1),
41+
(4, 4),
42+
),
43+
)
44+
def test_discrete_colormap_expands_scalar_color(N, expected_size):
45+
"""Scalar color strings produce the requested monochromatic colormap."""
46+
cmap = pcolors.DiscreteColormap("red", N=N)
47+
48+
assert cmap.N == expected_size
49+
assert [mcolors.to_hex(color) for color in cmap.colors] == [
50+
"#ff0000"
51+
] * expected_size
52+
assert cmap.monochrome is True
53+
54+
55+
def test_discrete_colormap_resizing_preserves_alpha_override():
56+
"""Alpha replacement is applied to every color after cycling the input."""
57+
cmap = pcolors.DiscreteColormap([(1, 0, 0, 0.2), (0, 0, 1, 0.8)], N=3, alpha=0.5)
58+
59+
assert [mcolors.to_hex(color, keep_alpha=True) for color in cmap.colors] == [
60+
"#ff000080",
61+
"#0000ff80",
62+
"#ff000080",
63+
]
64+
assert cmap.monochrome is False
65+
66+
67+
@pytest.mark.parametrize(
68+
("colors", "expected"),
69+
(
70+
(["red", "red"], True),
71+
(["red", "blue"], False),
72+
),
73+
)
74+
def test_discrete_colormap_monochrome_is_python_bool(colors, expected):
75+
"""Monochrome detection remains available across matplotlib versions."""
76+
cmap = pcolors.DiscreteColormap(colors)
77+
78+
assert cmap.monochrome is expected
79+
80+
1081
@pytest.fixture(autouse=True)
1182
def setup_teardown():
1283
"""

0 commit comments

Comments
 (0)