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

Skip to content

Commit 1b8866e

Browse files
committed
MNT: Remove cmap_d colormap access
1 parent a9dd8b9 commit 1b8866e

File tree

6 files changed

+82
-159
lines changed

6 files changed

+82
-159
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: 69 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

2020
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,9 @@ class ColormapRegistry(Mapping):
12583
"""
12684
def __init__(self, cmaps):
12785
self._cmaps = cmaps
86+
self._builtin_cmaps = tuple(cmaps)
87+
# A shim to allow register_cmap() to force an override
88+
self._allow_override_builtin = False
12889

12990
def __getitem__(self, item):
13091
try:
@@ -177,23 +138,66 @@ def register(self, cmap, *, name=None, force=False):
177138
registered name. True supports overwriting registered colormaps
178139
other than the builtin colormaps.
179140
"""
141+
_api.check_isinstance(colors.Colormap, cmap=cmap)
142+
180143
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())
144+
if name in self:
145+
if not force:
146+
# don't allow registering an already existing cmap
147+
# unless explicitly asked to
148+
raise ValueError(
149+
f'A colormap named "{name}" is already registered.')
150+
elif (name in self._builtin_cmaps
151+
and not self._allow_override_builtin):
152+
# We don't allow overriding a builtin unless privately
153+
# coming from register_cmap()
154+
raise ValueError("Re-registering the builtin cmap "
155+
f"{name!r} is not allowed.")
156+
157+
# Warn that we are updating an already exisiting colormap
158+
_api.warn_external(f"Overwriting the cmap {name!r} "
159+
"that was already in the registry.")
160+
161+
self._cmaps[name] = cmap.copy()
185162

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

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)
192195

193196
# public access to the colormaps should be via `matplotlib.colormaps`. For now,
194197
# we still create the registry here, but that should stay an implementation
195198
# detail.
196-
_colormaps = ColormapRegistry(_cmap_registry)
199+
_colormaps = ColormapRegistry(_gen_cmap_registry())
200+
globals().update(_colormaps)
197201

198202

199203
def register_cmap(name=None, cmap=None, *, override_builtin=False):
@@ -223,14 +227,6 @@ def register_cmap(name=None, cmap=None, *, override_builtin=False):
223227
colormap.
224228
225229
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-
234230
"""
235231
_api.check_isinstance((str, None), name=name)
236232
if name is None:
@@ -239,21 +235,12 @@ def register_cmap(name=None, cmap=None, *, override_builtin=False):
239235
except AttributeError as err:
240236
raise ValueError("Arguments must include a name or a "
241237
"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
238+
# override_builtin is allowed here for backward compatbility
239+
# this is just a shim to enable that to work privately in
240+
# the global ColormapRegistry
241+
_colormaps._allow_override_builtin = override_builtin
242+
_colormaps.register(cmap, name=name, force=override_builtin)
243+
_colormaps._allow_override_builtin = False
257244

258245

259246
def get_cmap(name=None, lut=None):
@@ -263,12 +250,6 @@ def get_cmap(name=None, lut=None):
263250
Colormaps added with :func:`register_cmap` take precedence over
264251
built-in colormaps.
265252
266-
Notes
267-
-----
268-
Currently, this returns the global colormap object. This is undesired
269-
because users could accidentally modify the global colormap.
270-
From Matplotlib 3.6 on, this will return a copy instead.
271-
272253
Parameters
273254
----------
274255
name : `matplotlib.colors.Colormap` or str or None, default: None
@@ -283,11 +264,11 @@ def get_cmap(name=None, lut=None):
283264
name = mpl.rcParams['image.cmap']
284265
if isinstance(name, colors.Colormap):
285266
return name
286-
_api.check_in_list(sorted(_cmap_registry), name=name)
267+
_api.check_in_list(sorted(_colormaps), name=name)
287268
if lut is None:
288-
return _cmap_registry[name]
269+
return _colormaps[name]
289270
else:
290-
return _cmap_registry[name]._resample(lut)
271+
return _colormaps[name]._resample(lut)
291272

292273

293274
def unregister_cmap(name):
@@ -321,14 +302,10 @@ def unregister_cmap(name):
321302
------
322303
ValueError
323304
If you try to de-register a default built-in colormap.
324-
325305
"""
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)
306+
cmap = _colormaps.get(name, None)
307+
_colormaps.unregister(name)
308+
return cmap
332309

333310

334311
class ScalarMappable:

lib/matplotlib/colors.py

Lines changed: 1 addition & 20 deletions
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
@@ -540,20 +539,6 @@ def _create_lookup_table(N, data, gamma=1.0):
540539
return np.clip(lut, 0.0, 1.0)
541540

542541

543-
def _warn_if_global_cmap_modified(cmap):
544-
if getattr(cmap, '_global', False):
545-
_api.warn_deprecated(
546-
"3.3",
547-
removal="3.6",
548-
message="You are modifying the state of a globally registered "
549-
"colormap. This has been deprecated since %(since)s and "
550-
"%(removal)s, you will not be able to modify a "
551-
"registered colormap in-place. To remove this warning, "
552-
"you can make a copy of the colormap first. "
553-
f'cmap = mpl.cm.get_cmap("{cmap.name}").copy()'
554-
)
555-
556-
557542
class Colormap:
558543
"""
559544
Baseclass for all scalar to RGBA mappings.
@@ -670,7 +655,6 @@ def __copy__(self):
670655
cmapobject.__dict__.update(self.__dict__)
671656
if self._isinit:
672657
cmapobject._lut = np.copy(self._lut)
673-
cmapobject._global = False
674658
return cmapobject
675659

676660
def __eq__(self, other):
@@ -692,7 +676,6 @@ def get_bad(self):
692676

693677
def set_bad(self, color='k', alpha=None):
694678
"""Set the color for masked values."""
695-
_warn_if_global_cmap_modified(self)
696679
self._rgba_bad = to_rgba(color, alpha)
697680
if self._isinit:
698681
self._set_extremes()
@@ -705,7 +688,6 @@ def get_under(self):
705688

706689
def set_under(self, color='k', alpha=None):
707690
"""Set the color for low out-of-range values."""
708-
_warn_if_global_cmap_modified(self)
709691
self._rgba_under = to_rgba(color, alpha)
710692
if self._isinit:
711693
self._set_extremes()
@@ -718,7 +700,6 @@ def get_over(self):
718700

719701
def set_over(self, color='k', alpha=None):
720702
"""Set the color for high out-of-range values."""
721-
_warn_if_global_cmap_modified(self)
722703
self._rgba_over = to_rgba(color, alpha)
723704
if self._isinit:
724705
self._set_extremes()
@@ -741,7 +722,7 @@ def with_extremes(self, *, bad=None, under=None, over=None):
741722
values and, when ``norm.clip = False``, low (*under*) and high (*over*)
742723
out-of-range values, have been set accordingly.
743724
"""
744-
new_cm = copy.copy(self)
725+
new_cm = self.copy()
745726
new_cm.set_extremes(bad=bad, under=under, over=over)
746727
return new_cm
747728

0 commit comments

Comments
 (0)
0