Skip to content

Commit 329983d

Browse files
committed
FIX: add missing method to ColormapRegistry
After putting pending deprecations on `cm.get_cmap` we discovered that downstream libraries (pandas) were using the deprecated method to normalize between `None` (to get the default colormap), strings, and Colormap instances. This adds a method to `ColormapRegistry` to do this normalization. This can not replace our internal helper due to variations in what exceptions are raised. Closes #23981
1 parent eb44018 commit 329983d

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

doc/api/prev_api_changes/api_changes_3.6.0/deprecations.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,13 @@ In Matplotlib 3.6 we have marked those top level functions as pending
5252
deprecation with the intention of deprecation in Matplotlib 3.7. The following
5353
functions have been marked for pending deprecation:
5454

55-
- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead
55+
- ``matplotlib.cm.get_cmap``; use ``matplotlib.colormaps[name]`` instead if you
56+
have a `str`.
57+
58+
**Added 3.6.1** Use `matplotlib.colors.ensure_colormap` if you have a string,
59+
`None` or a `matplotlib.colors.Colormap` object that you want to convert to a
60+
`matplotlib.colors.Colormap` instance. Raises `KeyError` rather than
61+
`ValueError` for missing strings.
5662
- ``matplotlib.cm.register_cmap``; use `matplotlib.colormaps.register
5763
<.ColormapRegistry.register>` instead
5864
- ``matplotlib.cm.unregister_cmap``; use `matplotlib.colormaps.unregister

lib/matplotlib/cm.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,49 @@ def unregister(self, name):
193193
"colormap.")
194194
self._cmaps.pop(name, None)
195195

196+
def ensure_colormap(self, cmap, *, default=None):
197+
"""
198+
Ensure that at given object is a converted to a color map.
199+
200+
If *cmap* in `None`, returns *default*
201+
202+
Parameters
203+
----------
204+
cmap : str, Colormap, None
205+
206+
- if a `Colormap`, return it
207+
- if a string, look it up in mpl.colormaps
208+
- if None, look up the default color map in mpl.colormaps
209+
210+
default : str, Colormap, None, optional
211+
The default color map be returned if *cmap* in `None`
212+
213+
If `None` defaults to :rc:`image.cmap`
214+
215+
Returns
216+
-------
217+
Colormap
218+
219+
Raises
220+
------
221+
KeyError
222+
"""
223+
if cmap is None:
224+
# if we have to fallback, call ourselves with the default value
225+
return self.ensure_colormap(
226+
# if the default default, use rcparams['image.cmap']
227+
default
228+
if default is not None
229+
else mpl.rcParams["image.cmap"]
230+
)
231+
232+
# if the user passed in a Colormap, simply return it
233+
if isinstance(cmap, colors.Colormap):
234+
return cmap
235+
236+
# otherwise, it must be a string so look it up
237+
return self[cmap]
238+
196239

197240
# public access to the colormaps should be via `matplotlib.colormaps`. For now,
198241
# we still create the registry here, but that should stay an implementation
@@ -281,7 +324,12 @@ def _get_cmap(name=None, lut=None):
281324
# pyplot.
282325
get_cmap = _api.deprecated(
283326
'3.6',
284-
name='get_cmap', pending=True, alternative="``matplotlib.colormaps[name]``"
327+
name='get_cmap',
328+
pending=True,
329+
alternative=(
330+
"``matplotlib.colormaps[name]`` " +
331+
"or ``matplotlib.colormaps.ensure_colormap(obj)``"
332+
)
285333
)(_get_cmap)
286334

287335

@@ -687,6 +735,8 @@ def _ensure_cmap(cmap):
687735
"""
688736
Ensure that we have a `.Colormap` object.
689737
738+
For internal use to preserve type stability of errors.
739+
690740
Parameters
691741
----------
692742
cmap : None, str, Colormap
@@ -698,6 +748,7 @@ def _ensure_cmap(cmap):
698748
Returns
699749
-------
700750
Colormap
751+
701752
"""
702753
if isinstance(cmap, colors.Colormap):
703754
return cmap

lib/matplotlib/tests/test_colors.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,24 @@ def test_register_cmap():
109109
cm.register_cmap('nome', cmap='not a cmap')
110110

111111

112+
def test_ensure_cmap():
113+
cr = mpl.colormaps
114+
new_cm = mcolors.ListedColormap(cr["viridis"].colors, name='v2')
115+
116+
# check None, str, and Colormap pass
117+
assert cr.ensure_colormap('plasma') == cr["plasma"]
118+
assert cr.ensure_colormap(cr["magma"]) == cr["magma"]
119+
120+
# check default default
121+
assert cr.ensure_colormap(None) == cr[mpl.rcParams['image.cmap']]
122+
# check setting default
123+
assert cr.ensure_colormap(None, default=new_cm) == new_cm
124+
assert cr.ensure_colormap(None, default='magma') == cr["magma"]
125+
bad_cmap = 'AardvarksAreAwkward'
126+
with pytest.raises(KeyError, match=bad_cmap):
127+
cr.ensure_colormap(bad_cmap)
128+
129+
112130
def test_double_register_builtin_cmap():
113131
name = "viridis"
114132
match = f"Re-registering the builtin cmap {name!r}."

0 commit comments

Comments
 (0)