8000 MNT: Remove cmap_d colormap access · matplotlib/matplotlib@d009a4b · GitHub
[go: up one dir, main page]

Skip to content

Commit d009a4b

Browse files
committed
MNT: Remove cmap_d colormap access
1 parent 80073f6 commit d009a4b

File tree

6 files changed

+67
-156
lines changed

6 files changed

+67
-156
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
``cmap_d`` removal
2+
~~~~~~~~~~~~~~~~~~
3+
4+
The deprecated ``matplotlib.cm.cmap_d`` access to global colormaps
5+
has been removed.

lib/matplotlib/backends/qt_editor/figureoptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ def prepare_data(d, init):
135135
continue
136136
labeled_mappables.append((label, mappable))
137137
mappables = []
138-
cmaps = [(cmap, name) for name, cmap in sorted(cm._cmap_registry.items())]
138+
cmaps = [(cmap, name) for name, cmap in sorted(cm._colormaps.items())]
139139
for label, mappable in labeled_mappables:
140140
cmap = mappable.get_cmap()
141-
if cmap not in cm._cmap_registry.values():
141+
if cmap not in cm._colormaps.values():
142142
cmaps = [(cmap, cmap.name), *cmaps]
143143
low, high = mappable.get_clim()
144144
mappabledata = [

lib/matplotlib/cm.py

Lines changed: 56 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
normalization.
1616
"""
1717

18-
from collections.abc import Mapping, MutableMapping
18+
from collections.abc import Mapping
1919

202 67E6 0
import numpy as np
2121
from numpy import ma
@@ -52,52 +52,10 @@ def _gen_cmap_registry():
5252
# Generate reversed cmaps.
5353
for cmap in list(cmap_d.values()):
5454
rmap = cmap.reversed()
55-
cmap._global = True
56-
rmap._global = True
5755
cmap_d[rmap.name] = rmap
5856
return cmap_d
5957

6058

61-
class _DeprecatedCmapDictWrapper(MutableMapping):
62-
"""Dictionary mapping for deprecated _cmap_d access."""
63-
64-
def __init__(self, cmap_registry):
65-
self._cmap_registry = cmap_registry
66-
67-
def __delitem__(self, key):
68-
self._warn_deprecated()
69-
self._cmap_registry.__delitem__(key)
70-
71-
def __getitem__(self, key):
72-
self._warn_deprecated()
73-
return self._cmap_registry.__getitem__(key)
74-
75-
def __iter__(self):
76-
self._warn_deprecated()
77-
return self._cmap_registry.__iter__()
78-
79-
def __len__(self):
80-
self._warn_deprecated()
81-
return self._cmap_registry.__len__()
82-
83-
def __setitem__(self, key, val):
84-
self._warn_deprecated()
85-
self._cmap_registry.__setitem__(key, val)
86-
87-
def get(self, key, default=None):
88-
self._warn_deprecated()
89-
return self._cmap_registry.get(key, default)
90-
91-
def _warn_deprecated(self):
92-
_api.warn_deprecated(
93-
"3.3",
94-
message="The global colormaps dictionary is no longer "
95-
"considered public API.",
96-
alternative="Please use register_cmap() and get_cmap() to "
97-
"access the contents of the dictionary."
98-
)
99-
100-
10159
class ColormapRegistry(Mapping):
10260
r"""
10361
Container for colormaps that are known to Matplotlib by name.
@@ -125,6 +83,7 @@ class ColormapRegistry(Mapping):
12583
"""
12684
def __init__(self, cmaps):
12785
self._cmaps = cmaps
86+
self._builtin_cmaps = tuple(cmaps)
12887

12988
def __getitem__(self, item):
13089
try:
@@ -177,23 +136,60 @@ def register(self, cmap, *, name=None, force=False):
177136
registered name. True supports overwriting registered colormaps
178137
other than the builtin colormaps.
179138
"""
139+
_api.check_isinstance(colors.Colormap, cmap=cmap)
140+
180141
name = name or cmap.name
181-
if name in self and not force:
182-
raise ValueError(
183-
f'A colormap named "{name}" is already registered.')
184-
register_cmap(name, cmap.copy())
142+
if name in self:
143+
if force:
144+
_api.warn_external(f"Trying to register the cmap {name!r} "
145+
"which already exists.")
146+
147+
else:
148+
raise ValueError(
149+
f"Trying to re-register the builtin cmap {name!r}."
150+
if name in self._builtin_cmaps else
151+
f'A colormap named "{name}" is already registered.')
152+
153+
self._cmaps[name] = cmap.copy()
185154

155+
def unregister(self, name):
156+
"""
157+
Remove a colormap from the registry.
158+
159+
You cannot remove built-in colormaps.
160+
161+
If the named colormap is not registered, returns with no error, raises
162+
if you try to de-register a default colormap.
163+
164+
.. warning::
165+
166+
Colormap names are currently a shared namespace that may be used
167+
by multiple packages. Use `unregister` only if you know you
168+
have registered that name before. In particular, do not
169+
unregister just in case to clean the name before registering a
170+
new colormap.
171+
172+
Parameters
173+
----------
174+
name : str
175+
The name of the colormap to be removed.
176+
177+
Raises
178+
------
179+
ValueError
180+
If you try to remove a default built-in colormap.
181+
"""
182+
if name in self._builtin_cmaps:
183+
raise ValueError(f"cannot unregister {name!r} which is a builtin "
184+
"colormap.")
185+
self._cmaps.pop(name, None)
186186

187-
_cmap_registry = _gen_cmap_registry()
188-
globals().update(_cmap_registry)
189-
# This is no longer considered public API
190-
cmap_d = _DeprecatedCmapDictWrapper(_cmap_registry)
191-
__builtin_cmaps = tuple(_cmap_registry)
192187

193188
# public access to the colormaps should be via `matplotlib.colormaps`. For now,
194189
# we still create the registry here, but that should stay an implementation
195190
# detail.
196-
_colormaps = ColormapRegistry(_cmap_registry)
191+
_colormaps = ColormapRegistry(_gen_cmap_registry())
192+
globals().update(_colormaps)
197193

198194

199195
def register_cmap(name=None, cmap=None, *, override_builtin=False):
@@ -223,14 +219,6 @@ def register_cmap(name=None, cmap=None, *, override_builtin=False):
223219
colormap.
224220
225221
Please do not use this unless you are sure you need it.
226-
227-
Notes
228-
-----
229-
Registering a colormap stores a reference to the colormap object
230-
which can currently be modified and inadvertently change the global
231-
colormap state. This behavior is deprecated and in Matplotlib 3.5
232-
the registered colormap will be immutable.
233-
234222
"""
235223
_api.check_isinstance((str, None), name=name)
236224
if name is None:
@@ -239,21 +227,7 @@ def register_cmap(name=None, cmap=None, *, override_builtin=False):
239227
except AttributeError as err:
240228
raise ValueError("Arguments must include a name or a "
241229
"Colormap") from err
242-
if name in _cmap_registry:
243-
if not override_builtin and name in __builtin_cmaps:
244-
msg = f"Trying to re-register the builtin cmap {name!r}."
245-
raise ValueError(msg)
246-
else:
247-
msg = f"Trying to register the cmap {name!r} which already exists."
248-
_api.warn_external(msg)
249-
250-
if not isinstance(cmap, colors.Colormap):
251-
raise ValueError("You must pass a Colormap instance. "
252-
f"You passed {cmap} a {type(cmap)} object.")
253-
254-
cmap._global = True
255-
_cmap_registry[name] = cmap
256-
return
230+
_colormaps.register(cmap, name=name, force=override_builtin)
257231

258232

259233
def get_cmap(name=None, lut=None):
@@ -263,12 +237,6 @@ def get_cmap(name=None, lut=None):
263237
Colormaps added with :func:`register_cmap` take precedence over
264238
built-in colormaps.
265239
266-
Notes
267-
-----
268-
Currently, this returns the global colormap object, which is deprecated.
269-
In Matplotlib 3.5, you will no longer be able to modify the global
270-
colormaps in-place.
271-
272240
Parameters
273241
----------
274242
name : `matplotlib.colors.Colormap` or str or None, default: None
@@ -283,11 +251,11 @@ def get_cmap(name=None, lut=None):
283251
name = mpl.rcParams['image.cmap']
284252
if isinstance(name, colors.Colormap):
285253
return name
286-
_api.check_in_list(sorted(_cmap_registry), name=name)
254+
_api.check_in_list(sorted(_colormaps), name=name)
287255
if lut is None:
288-
return _cmap_registry[name]
256+
return _colormaps[name]
289257
else:
290-
return _cmap_registry[name]._resample(lut)
258+
return _colormaps[name]._resample(lut)
291259

292260

293261
def unregister_cmap(name):
@@ -321,14 +289,10 @@ def unregister_cmap(name):
321289
------
322290
ValueError
323291
If you try to de-register a default built-in colormap.
324-
325292
"""
326-
if name not in _cmap_registry:
327-
return
328-
if name in __builtin_cmaps:
329-
raise ValueError(f"cannot unregister {name!r} which is a builtin "
330-
"colormap.")
331-
return _cmap_registry.pop(name)
293+
cmap = _colormaps.get(name, None)
294+
_colormaps.unregister(name)
295+
return cmap
332296

333297

334298
class ScalarMappable:

lib/matplotlib/colors.py

Lines changed: 1 addition & 20 deletions
10000
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
import base64
4343
from collections.abc import Sized, Sequence
44-
import copy
4544
import functools
4645
import inspect
4746
import io
@@ -534,20 +533,6 @@ def _create_lookup_table(N, data, gamma=1.0):
534533
return np.clip(lut, 0.0, 1.0)
535534

536535

537-
def _warn_if_global_cmap_modified(cmap):
538-
if getattr(cmap, '_global', False):
539-
_api.warn_deprecated(
540-
"3.3",
541-
removal="3.6",
542-
message="You are modifying the state of a globally registered "
543-
"colormap. This has been deprecated since %(since)s and "
544-
"%(removal)s, you will not be able to modify a "
545-
"registered colormap in-place. To remove this warning, "
546-
"you can make a copy of the colormap first. "
547-
f'cmap = mpl.cm.get_cmap("{cmap.name}").copy()'
548-
)
549-
550-
551536
class Colormap:
552537
"""
553538
Baseclass for all scalar to RGBA mappings.
@@ -664,7 +649,6 @@ def __copy__(self):
664649
cmapobject.__dict__.update(self.__dict__)
665650
if self._isinit:
666651
cmapobject._lut = np.copy(self._lut)
667-
cmapobject._global = False
668652
return cmapobject
669653

670654
def __eq__(self, other):
@@ -686,7 +670,6 @@ def get_bad(self):
686670

687671
def set_bad(self, color='k', alpha=None):
688672
"""Set the color for masked values."""
689-
_warn_if_global_cmap_modified(self)
690673
self._rgba_bad = to_rgba(color, alpha)
691674
if self._isinit:
692675
self._set_extremes()
@@ -699,7 +682,6 @@ def get_under(self):
699682

700683
def set_under(self, color='k', alpha=None):
701684
"""Set the color for low out-of-range values."""
702-
_warn_if_global_cmap_modified(self)
703685
self._rgba_under = to_rgba(color, alpha)
704686
if self._isinit:
705687
self._set_extremes()
@@ -712,7 +694,6 @@ def get_over(self):
712694

713695
def set_over(self, color='k', alpha=None):
714696
"""Set the color for high out-of-range values."""
715-
_warn_if_global_cmap_modified(self)
716697
self._rgba_over = to_rgba(color, alpha)
717698
if self._isinit:
718699
self._set_extremes()
@@ -735,7 +716,7 @@ def with_extremes(self, *, bad=None, under=None, over=None):
735716
values and, when ``norm.clip = False``, low (*under*) and high (*over*)
736717
out-of-range values, have been set accordingly.
737718
"""
738-
new_cm = copy.copy(self)
719+
new_cm = self.copy()
739720
new_cm.set_extremes(bad=bad, under=under, over=over)
740721
return new_cm
741722

lib/matplotlib/tests/test_colors.py

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def test_resample():
6666

6767

6868
def test_register_cmap():
69-
new_cm = copy.copy(cm.get_cmap("viridis"))
69+
new_cm = cm.get_cmap("viridis")
7070
target = "viridis2"
7171
cm.register_cmap(target, new_cm)
7272
assert plt.get_cmap(target) == new_cm
@@ -75,17 +75,14 @@ def test_register_cmap():
7575
match="Arguments must include a name or a Colormap"):
7676
cm.register_cmap()
7777

78-
with pytest.warns(UserWarning):
79-
cm.register_cmap(target, new_cm)
80-
8178
cm.unregister_cmap(target)
8279
with pytest.raises(ValueError,
8380
match=f'{target!r} is not a valid value for name;'):
8481
cm.get_cmap(target)
8582
# test that second time is error free
8683
cm.unregister_cmap(target)
8784

88-
with pytest.raises(ValueError, match="You must pass a Colormap instance."):
85+
with pytest.raises(TypeError, match="'cmap' must be"):
8986
cm.register_cmap('nome', cmap='not a cmap')
9087

9188

@@ -105,42 +102,6 @@ def test_unregister_builtin_cmap():
105102
cm.unregister_cmap(name)
106103

107104

108-
def test_colormap_global_set_warn():
109-
new_cm = plt.get_cmap('viridis')
110-
# Store the old value so we don't override the state later on.
111-
orig_cmap = copy.copy(new_cm)
112-
with pytest.warns(cbook.MatplotlibDeprecationWarning,
113-
match="You are modifying the state of a globally"):
114-
# This should warn now because we've modified the global state
115-
new_cm.set_under('k')
116-
117-
# This shouldn't warn because it is a copy
118-
copy.copy(new_cm).set_under('b')
119-
120-
# Test that registering and then modifying warns
121-
plt.register_cmap(name='test_cm', cmap=copy.copy(orig_cmap))
122-
new_cm = plt.get_cmap('test_cm')
123-
with pytest.warns(cbook.MatplotlibDeprecationWarning,
124-
match="You are modifying the state of a globally"):
125-
# This should warn now because we've modified the global state
126-
new_cm.set_under('k')
127-
128-
# Re-register the original
129-
with pytest.warns(UserWarning):
130-
plt.register_cmap(cmap=orig_cmap, override_builtin=True)
131-
132-
133-
def test_colormap_dict_deprecate():
134-
# Make sure we warn on get and set access into cmap_d
135-
with pytest.warns(cbook.MatplotlibDeprecationWarning,
136-
match="The global colormaps dictionary is no longer"):
137-
cmap = plt.cm.cmap_d['viridis']
138-
139-
with pytest.warns(cbook.MatplotlibDeprecationWarning,
140-
match="The global colormaps dictionary is no longer"):
141-
plt.cm.cmap_d['test'] = cmap
142-
143-
144105
def test_colormap_copy():
145106
cmap = plt.cm.Reds
146107
copied_cmap = copy.copy(cmap)

lib/matplotlib/tests/test_pickle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def test_inset_and_secondary():
201201
pickle.loads(pickle.dumps(fig))
202202

203203

204-
@pytest.mark.parametrize("cmap", cm._cmap_registry.values())
204+
@pytest.mark.parametrize("cmap", cm._colormaps.values())
205205
def test_cmap(cmap):
206206
pickle.dumps(cmap)
207207

0 commit comments

Comments
 (0)
0