Skip to content

Commit 333bc06

Browse files
committed
Changing get_cmap to return copies of the registered colormaps.
1 parent c292827 commit 333bc06

File tree

4 files changed

+49
-12
lines changed

4 files changed

+49
-12
lines changed

doc/api/next_api_changes/behaviour.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ deprecation warning.
9898
`~.Axes.errorbar` now color cycles when only errorbar color is set
9999
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
100100

101-
Previously setting the *ecolor* would turn off automatic color cycling for the plot, leading to the
102-
the lines and markers defaulting to whatever the first color in the color cycle was in the case of
103-
multiple plot calls.
101+
Previously setting the *ecolor* would turn off automatic color cycling for the plot, leading to the
102+
the lines and markers defaulting to whatever the first color in the color cycle was in the case of
103+
multiple plot calls.
104104

105105
`.rcsetup.validate_color_for_prop_cycle` now always raises TypeError for bytes input
106106
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -147,3 +147,11 @@ The parameter ``s`` to `.Axes.annotate` and `.pyplot.annotate` is renamed to
147147
The old parameter name remains supported, but
148148
support for it will be dropped in a future Matplotlib release.
149149

150+
`pyplot.get_cmap()` now returns a copy of the colormap
151+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
152+
Previously, calling ``.pyplot.get_cmap()`` would return a pointer to
153+
the built-in Colormap. If you made modifications to that colormap, the
154+
changes would be propagated in the global state. This function now
155+
returns a copy of all registered colormaps to keep the built-in
156+
colormaps untouched.
157+

lib/matplotlib/cm.py

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

18+
import copy
1819
import functools
1920

2021
import numpy as np
@@ -70,7 +71,7 @@ def _gen_cmap_d():
7071

7172

7273
cmap_d = _gen_cmap_d()
73-
locals().update(cmap_d)
74+
locals().update(copy.deepcopy(cmap_d))
7475

7576

7677
# Continue with definitions ...
@@ -95,6 +96,9 @@ def register_cmap(name=None, cmap=None, data=None, lut=None):
9596
and the resulting colormap is registered. Instead of this implicit
9697
colormap creation, create a `.LinearSegmentedColormap` and use the first
9798
case: ``register_cmap(cmap=LinearSegmentedColormap(name, data, lut))``.
99+
100+
If *name* is the same as a built-in colormap this will replace the
101+
built-in Colormap of the same name.
98102
"""
99103
cbook._check_isinstance((str, None), name=name)
100104
if name is None:
@@ -124,26 +128,27 @@ def get_cmap(name=None, lut=None):
124128
"""
125129
Get a colormap instance, defaulting to rc values if *name* is None.
126130
127-
Colormaps added with :func:`register_cmap` take precedence over
128-
built-in colormaps.
131+
Colormaps added with :func:`register_cmap` with the same name as
132+
built-in colormaps will replace them.
129133
130134
Parameters
131135
----------
132136
name : `matplotlib.colors.Colormap` or str or None, default: None
133-
If a `.Colormap` instance, it will be returned. Otherwise, the name of
134-
a colormap known to Matplotlib, which will be resampled by *lut*. The
135-
default, None, means :rc:`image.cmap`.
137+
If a `.Colormap` instance, a copy of it will be returned.
138+
Otherwise, the name of a colormap known to Matplotlib, which will
139+
be resampled by *lut*. A copy of the requested Colormap is always
140+
returned. The default, None, means :rc:`image.cmap`.
136141
lut : int or None, default: None
137142
If *name* is not already a Colormap instance and *lut* is not None, the
138143
colormap will be resampled to have *lut* entries in the lookup table.
139144
"""
140145
if name is None:
141146
name = mpl.rcParams['image.cmap']
142147
if isinstance(name, colors.Colormap):
143-
return name
148+
return copy.copy(name)
144149
cbook._check_in_list(sorted(cmap_d), name=name)
145150
if lut is None:
146-
return cmap_d[name]
151+
return copy.copy(cmap_d[name])
147152
else:
148153
return cmap_d[name]._resample(lut)
149154

lib/matplotlib/colors.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,18 @@ def __copy__(self):
601601
cmapobject._lut = np.copy(self._lut)
602602
return cmapobject
603603

604+
def __eq__(self, other):
605+
if isinstance(other, Colormap):
606+
# To compare lookup tables the Colormaps have to be initialized
607+
if not self._isinit:
608+
self._init()
609+
if not other._isinit:
610+
other._init()
611+
if self._lut.shape != other._lut.shape:
612+
return False
613+
return np.all(self._lut == other._lut)
614+
return False
615+
604616
def set_bad(self, color='k', alpha=None):
605617
"""Set the color for masked values."""
606618
self._rgba_bad = to_rgba(color, alpha)

lib/matplotlib/tests/test_colors.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def test_resample():
6161

6262

6363
def test_register_cmap():
64-
new_cm = copy.copy(plt.cm.viridis)
64+
new_cm = plt.get_cmap('viridis')
6565
cm.register_cmap('viridis2', new_cm)
6666
assert plt.get_cmap('viridis2') == new_cm
6767

@@ -70,6 +70,18 @@ def test_register_cmap():
7070
cm.register_cmap()
7171

7272

73+
def test_colormap_builtin_immutable():
74+
new_cm = plt.get_cmap('viridis')
75+
new_cm.set_over('b')
76+
# Make sure that this didn't mess with the original viridis cmap
77+
assert new_cm != plt.get_cmap('viridis')
78+
79+
# Also test that pyplot access doesn't mess the original up
80+
new_cm = plt.cm.viridis
81+
new_cm.set_over('b')
82+
assert new_cm != plt.get_cmap('viridis')
83+
84+
7385
def test_colormap_copy():
7486
cm = plt.cm.Reds
7587
cm_copy = copy.copy(cm)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy