Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4915f44

Browse filesBrowse files
committed
Warning when modifying the state of a global colormap.
1 parent b94812c commit 4915f44
Copy full SHA for 4915f44

File tree

Expand file treeCollapse file tree

3 files changed

+94
-10
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+94
-10
lines changed

‎lib/matplotlib/cm.py

Copy file name to clipboardExpand all lines: lib/matplotlib/cm.py
+19-10Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,16 @@ def _gen_cmap_d():
6565
# Generate reversed cmaps.
6666
for cmap in list(cmap_d.values()):
6767
rmap = cmap.reversed()
68+
cmap._global = True
69+
rmap._global = True
6870
cmap_d[rmap.name] = rmap
6971
return cmap_d
7072

7173

72-
cmap_d = _gen_cmap_d()
73-
locals().update(cmap_d)
74+
_cmap_d = _gen_cmap_d()
75+
locals().update(_cmap_d)
76+
# This is no longer considered public API
77+
cmap_d = _cmap_d
7478

7579

7680
# Continue with definitions ...
@@ -104,7 +108,8 @@ def register_cmap(name=None, cmap=None, data=None, lut=None):
104108
raise ValueError("Arguments must include a name or a "
105109
"Colormap") from err
106110
if isinstance(cmap, colors.Colormap):
107-
cmap_d[name] = cmap
111+
cmap._global = True
112+
_cmap_d[name] = cmap
108113
return
109114
if lut is not None or data is not None:
110115
cbook.warn_deprecated(
@@ -117,7 +122,8 @@ def register_cmap(name=None, cmap=None, data=None, lut=None):
117122
if lut is None:
118123
lut = mpl.rcParams['image.lut']
119124
cmap = colors.LinearSegmentedColormap(name, data, lut)
120-
cmap_d[name] = cmap
125+
cmap._global = True
126+
_cmap_d[name] = cmap
121127

122128

123129
def get_cmap(name=None, lut=None):
@@ -130,9 +136,12 @@ def get_cmap(name=None, lut=None):
130136
Parameters
131137
----------
132138
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`.
139+
If a `.Colormap` instance, it will be returned.
140+
Otherwise, the name of a colormap known to Matplotlib, which will
141+
be resampled by *lut*. Currently, this returns the global colormap
142+
object, which is deprecated. In Matplotlib 3.5, you will no longer be
143+
able to modify the global colormaps in-place.
144+
The default, None, means :rc:`image.cmap`.
136145
lut : int or None, default: None
137146
If *name* is not already a Colormap instance and *lut* is not None, the
138147
colormap will be resampled to have *lut* entries in the lookup table.
@@ -141,11 +150,11 @@ def get_cmap(name=None, lut=None):
141150
name = mpl.rcParams['image.cmap']
142151
if isinstance(name, colors.Colormap):
143152
return name
144-
cbook._check_in_list(sorted(cmap_d), name=name)
153+
cbook._check_in_list(sorted(_cmap_d), name=name)
145154
if lut is None:
146-
return cmap_d[name]
155+
return _cmap_d[name]
147156
else:
148-
return cmap_d[name]._resample(lut)
157+
return _cmap_d[name]._resample(lut)
149158

150159

151160
class ScalarMappable:

‎lib/matplotlib/colors.py

Copy file name to clipboardExpand all lines: lib/matplotlib/colors.py
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,21 @@ def makeMappingArray(N, data, gamma=1.0):
484484
return _create_lookup_table(N, data, gamma)
485485

486486

487+
def _deprecate_global_cmap(cmap):
488+
if hasattr(cmap, '_global') and cmap._global:
489+
cbook.warn_deprecated(
490+
"3.3",
491+
message="You are modifying the state of a globally registered "
492+
"colormap. In future versions, you will not be able "
493+
"to modify a globally registered colormap directly. "
494+
"To eliminate this warning until then, you can make "
495+
"a copy of the requested colormap before modifying it. ",
496+
alternative="To modify a colormap without overwriting the "
497+
"global state, you can make a copy of the colormap "
498+
f"first. cmap = copy.copy(mpl.cm.get_cmap({cmap.name})"
499+
)
500+
501+
487502
class Colormap:
488503
"""
489504
Baseclass for all scalar to RGBA mappings.
@@ -599,13 +614,15 @@ def __copy__(self):
599614
cmapobject.__dict__.update(self.__dict__)
600615
if self._isinit:
601616
cmapobject._lut = np.copy(self._lut)
617+
cmapobject._global = False
602618
return cmapobject
603619

604620
def set_bad(self, color='k', alpha=None):
605621
"""Set the color for masked values."""
606622
self._rgba_bad = to_rgba(color, alpha)
607623
if self._isinit:
608624
self._set_extremes()
625+
_deprecate_global_cmap(self)
609626

610627
def set_under(self, color='k', alpha=None):
611628
"""
@@ -614,6 +631,7 @@ def set_under(self, color='k', alpha=None):
614631
self._rgba_under = to_rgba(color, alpha)
615632
if self._isinit:
616633
self._set_extremes()
634+
_deprecate_global_cmap(self)
617635

618636
def set_over(self, color='k', alpha=None):
619637
"""
@@ -622,6 +640,7 @@ def set_over(self, color='k', alpha=None):
622640
self._rgba_over = to_rgba(color, alpha)
623641
if self._isinit:
624642
self._set_extremes()
643+
_deprecate_global_cmap(self)
625644

626645
def _set_extremes(self):
627646
if self._rgba_under:

‎lib/matplotlib/tests/test_colors.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_colors.py
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,62 @@ def test_register_cmap():
7070
cm.register_cmap()
7171

7272

73+
@pytest.mark.skipif(matplotlib.__version__ < "3.5",
74+
reason="This test modifies the global state of colormaps.")
75+
def test_colormap_global_unchanged_state():
76+
new_cm = plt.get_cmap('viridis')
77+
new_cm.set_over('b')
78+
# Make sure that this didn't mess with the original viridis cmap
79+
assert new_cm != plt.get_cmap('viridis')
80+
81+
# Also test that pyplot access doesn't mess the original up
82+
new_cm = plt.cm.viridis
83+
new_cm.set_over('b')
84+
assert new_cm != plt.get_cmap('viridis')
85+
86+
87+
@pytest.mark.skipif(matplotlib.__version__ < "3.4",
88+
reason="This test modifies the global state of colormaps.")
89+
def test_colormap_global_get_warn():
90+
new_cm = plt.get_cmap('viridis')
91+
# Store the old value so we don't override the state later on.
92+
orig_cmap = copy.copy(new_cm)
93+
new_cm.set_under('k')
94+
with pytest.warns(cbook.MatplotlibDeprecationWarning,
95+
match="You are modifying the state of a globally"):
96+
# This should warn now because we've modified the global state
97+
# without registering it
98+
plt.get_cmap('viridis')
99+
100+
# Test that re-registering the original cmap clears the warning
101+
plt.register_cmap(cmap=orig_cmap)
102+
plt.get_cmap('viridis')
103+
104+
105+
def test_colormap_global_set_warn():
106+
new_cm = plt.get_cmap('viridis')
107+
# Store the old value so we don't override the state later on.
108+
orig_cmap = copy.copy(new_cm)
109+
with pytest.warns(cbook.MatplotlibDeprecationWarning,
110+
match="You are modifying the state of a globally"):
111+
# This should warn now because we've modified the global state
112+
new_cm.set_under('k')
113+
114+
# This shouldn't warn because it is a copy
115+
copy.copy(new_cm).set_under('b')
116+
117+
# Test that registering and then modifying warns
118+
plt.register_cmap(name='test_cm', cmap=copy.copy(orig_cmap))
119+
new_cm = plt.get_cmap('test_cm')
120+
with pytest.warns(cbook.MatplotlibDeprecationWarning,
121+
match="You are modifying the state of a globally"):
122+
# This should warn now because we've modified the global state
123+
new_cm.set_under('k')
124+
125+
# Re-register the original
126+
plt.register_cmap(cmap=orig_cmap)
127+
128+
73129
def test_colormap_copy():
74130
cm = plt.cm.Reds
75131
cm_copy = copy.copy(cm)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.