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 0f59282

Browse filesBrowse files
committed
MNT: Remove deprecated axes kwargs collision detection
In Matplotlib 2.1, the behavior of reusing existing axes when created with the same arguments was deprecated (see #9037). This behavior is now removed. Functions that create new axes (`axes`, `add_axes`, `subplot`, etc.) will now always create new axes, regardless of whether the kwargs passed to them match already existing axes. Passing kwargs to `gca` is deprecated. If `gca` is called with kwargs that do not match the current axes, then an exception is raised. Fixes #18832.
1 parent 48031d8 commit 0f59282
Copy full SHA for 0f59282

File tree

Expand file treeCollapse file tree

7 files changed

+104
-110
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+104
-110
lines changed
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pyplot.gca()
2+
~~~~~~~~~~~~
3+
4+
Passing keyword arguments to ``.pyplot.gca`` will not be supported in a future
5+
release.
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Changes to _AxesStack, preparing for its removal
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The behavior of the internal ``.figure._AxesStack`` class has changed
5+
significantly in the process of removing the old behavior of gca() with regard
6+
to keyword arguments. When the deprecated behavior has been fully removed and
7+
gca() no longer takes keyword arguments, the ``.figure._AxesStack`` class will
8+
be removed.
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Changes to behavior of Axes creation methods (gca(), add_axes(), add_subplot())
2+
-------------------------------------------------------------------------------
3+
4+
The behavior of the functions to create new axes (``.pyplot.subplot``,
5+
``.figure.Figure.add_axes``, ``.figure.Figure.add_subplot``) has changed. In
6+
the past, these functions would detect if you were attempting to create Axes
7+
with the same keyword arguments as already-existing axes in the current figure,
8+
and if so, they would return the existing Axes. Now, these functions will
9+
always create new Axes.
10+
11+
Correspondingly, the behavior of the functions to get the current Axes
12+
(``.pyplot.gca``, ``.figure.Figure.gca``) has changed. In the past, these
13+
functions accepted keyword arguments. If the keyword arguments matched an
14+
already-existing Axes, then that Axes would be returned, otherwise new Axes
15+
would be created with those keyword arguments. Now, an exception is raised if
16+
there are Axes and the current Axes were not created with the same keyword
17+
arguments. In a future release, these functions will not accept keyword
18+
arguments at all.

‎lib/matplotlib/figure.py

Copy file name to clipboardExpand all lines: lib/matplotlib/figure.py
+24-87Lines changed: 24 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -56,48 +56,22 @@ class _AxesStack(cbook.Stack):
5656
Specialization of `.Stack`, to handle all tracking of `~.axes.Axes` in a
5757
`.Figure`.
5858
59-
This stack stores ``key, (ind, axes)`` pairs, where:
60-
61-
* **key** is a hash of the args and kwargs used in generating the Axes.
62-
* **ind** is a serial index tracking the order in which Axes were added.
59+
This stack stores ``key, axes`` pairs, where **key** is a hash of the args
60+
and kwargs used in generating the Axes.
6361
6462
AxesStack is a callable; calling it returns the current Axes.
6563
The `current_key_axes` method returns the current key and associated Axes.
6664
"""
6765

68-
def __init__(self):
69-
super().__init__()
70-
self._ind = 0
71-
7266
def as_list(self):
7367
"""
7468
Return a list of the Axes instances that have been added to the figure.
7569
"""
76-
ia_list = [a for k, a in self._elements]
77-
ia_list.sort()
78-
return [a for i, a in ia_list]
79-
80-
def get(self, key):
81-
"""
82-
Return the Axes instance that was added with *key*.
83-
If it is not present, return *None*.
84-
"""
85-
item = dict(self._elements).get(key)
86-
if item is None:
87-
return None
88-
cbook.warn_deprecated(
89-
"2.1",
90-
message="Adding an axes using the same arguments as a previous "
91-
"axes currently reuses the earlier instance. In a future "
92-
"version, a new instance will always be created and returned. "
93-
"Meanwhile, this warning can be suppressed, and the future "
94-
"behavior ensured, by passing a unique label to each axes "
95-
"instance.")
96-
return item[1]
70+
return [a for k, a in self._elements]
9771

9872
def _entry_from_axes(self, e):
99-
ind, k = {a: (ind, k) for k, (ind, a) in self._elements}[e]
100-
return (k, (ind, e))
73+
k = {a: k for k, a in self._elements}[e]
74+
return (k, e)
10175

10276
def remove(self, a):
10377
"""Remove the Axes from the stack."""
@@ -114,45 +88,24 @@ def add(self, key, a):
11488
"""
11589
Add Axes *a*, with key *key*, to the stack, and return the stack.
11690
117-
If *key* is unhashable, replace it by a unique, arbitrary object.
118-
11991
If *a* is already on the stack, don't add it again, but
12092
return *None*.
12193
"""
12294
# All the error checking may be unnecessary; but this method
12395
# is called so seldom that the overhead is negligible.
12496
cbook._check_isinstance(Axes, a=a)
125-
try:
126-
hash(key)
127-
except TypeError:
128-
key = object()
129-
130-
a_existing = self.get(key)
131-
if a_existing is not None:
132-
super().remove((key, a_existing))
133-
cbook._warn_external(
134-
"key {!r} already existed; Axes is being replaced".format(key))
135-
# I don't think the above should ever happen.
136-
137-
if a in self:
138-
return None
139-
self._ind += 1
140-
return super().push((key, (self._ind, a)))
97+
return super().push((key, a))
14198

14299
def current_key_axes(self):
143100
"""
144101
Return a tuple of ``(key, axes)`` for the active Axes.
145102
146103
If no Axes exists on the stack, then returns ``(None, None)``.
147104
"""
148-
if not len(self._elements):
149-
return self._default, self._default
150-
else:
151-
key, (index, axes) = self._elements[self._pos]
152-
return key, axes
105+
return super().__call__() or (None, None)
153106

154107
def __call__(self):
155-
return self.current_key_axes()[1]
108+
ka = self.current_key_axes()[1]
156109

157110
def __contains__(self, a):
158111
return a in self.as_list()
@@ -660,15 +613,8 @@ def add_axes(self, *args, **kwargs):
660613
"add_axes() got multiple values for argument 'rect'")
661614
args = (kwargs.pop('rect'), )
662615

663-
# shortcut the projection "key" modifications later on, if an axes
664-
# with the exact args/kwargs exists, return it immediately.
665-
key = self._make_key(*args, **kwargs)
666-
ax = self._axstack.get(key)
667-
if ax is not None:
668-
self.sca(ax)
669-
return ax
670-
671616
if isinstance(args[0], Axes):
617+
key = self._make_key(*args, **kwargs)
672618
a = args[0]
673619
if a.get_figure() is not self:
674620
raise ValueError(
@@ -681,13 +627,6 @@ def add_axes(self, *args, **kwargs):
681627
projection_class, kwargs, key = \
682628
self._process_projection_requirements(*args, **kwargs)
683629

684-
# check that an axes of this type doesn't already exist, if it
685-
# does, set it as active and return it
686-
ax = self._axstack.get(key)
687-
if isinstance(ax, projection_class):
688-
self.sca(ax)
689-
return ax
690-
691630
# create the new axes using the axes class given
692631
a = projection_class(self, rect, **kwargs)
693632
return self._add_axes_internal(key, a)
@@ -832,19 +771,6 @@ def add_subplot(self, *args, **kwargs):
832771
args = tuple(map(int, str(args[0])))
833772
projection_class, kwargs, key = \
834773
self._process_projection_requirements(*args, **kwargs)
835-
ax = self._axstack.get(key) # search axes with this key in stack
836-
if ax is not None:
837-
if isinstance(ax, projection_class):
838-
# the axes already existed, so set it as active & return
839-
self.sca(ax)
840-
return ax
841-
else:
842-
# Undocumented convenience behavior:
843-
# subplot(111); subplot(111, projection='polar')
844-
# will replace the first with the second.
845-
# Without this, add_subplot would be simpler and
846-
# more similar to add_axes.
847-
self._axstack.remove(ax)
848774
ax = subplot_class_factory(projection_class)(self, *args, **kwargs)
849775
return self._add_axes_internal(key, ax)
850776

@@ -1569,6 +1495,16 @@ def gca(self, **kwargs):
15691495
%(Axes)s
15701496
15711497
"""
1498+
if kwargs:
1499+
cbook.warn_deprecated(
1500+
"3.4",
1501+
message="Calling gca() with keyword arguments is deprecated. "
1502+
"In a future version, gca() will take no keyword arguments. "
1503+
"The gca() function should only be used to get the current "
1504+
"axes, or if no axes exist, create new axes with default "
1505+
"keyword arguments. To create a new axes with non-default "
1506+
"arguments, use plt.axes() or plt.subplot().")
1507+
15721508
ckey, cax = self._axstack.current_key_axes()
15731509
# if there exists an axes on the stack see if it matches
15741510
# the desired axes configuration
@@ -1595,10 +1531,11 @@ def gca(self, **kwargs):
15951531
if key == ckey and isinstance(cax, projection_class):
15961532
return cax
15971533
else:
1598-
cbook._warn_external('Requested projection is different '
1599-
'from current axis projection, '
1600-
'creating new axis with requested '
1601-
'projection.')
1534+
raise ValueError(
1535+
"The arguments passed to gca() did not match the "
1536+
"arguments with which the current axes were "
1537+
"originally created. To create new axes, use "
1538+
"axes() or subplot().")
16021539

16031540
# no axes found, so create one which spans the figure
16041541
return self.add_subplot(1, 1, 1, **kwargs)

‎lib/matplotlib/pyplot.py

Copy file name to clipboardExpand all lines: lib/matplotlib/pyplot.py
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,10 +2410,13 @@ def polar(*args, **kwargs):
24102410
"""
24112411
# If an axis already exists, check if it has a polar projection
24122412
if gcf().get_axes():
2413-
if not isinstance(gca(), PolarAxes):
2413+
ax = gca()
2414+
if isinstance(ax, PolarAxes):
2415+
return ax
2416+
else:
24142417
cbook._warn_external('Trying to create polar plot on an axis '
24152418
'that does not have a polar projection.')
2416-
ax = gca(polar=True)
2419+
ax = axes(polar=True)
24172420
ret = ax.plot(*args, **kwargs)
24182421
return ret
24192422

‎lib/matplotlib/tests/test_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_axes.py
+20-12Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,28 +2387,36 @@ def _as_mpl_axes(self):
23872387
# testing axes creation with plt.axes
23882388
ax = plt.axes([0, 0, 1, 1], projection=prj)
23892389
assert type(ax) == PolarAxes
2390-
ax_via_gca = plt.gca(projection=prj)
2390+
with pytest.warns(
2391+
MatplotlibDeprecationWarning,
2392+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
2393+
ax_via_gca = plt.gca(projection=prj)
23912394
assert ax_via_gca is ax
23922395
plt.close()
23932396

23942397
# testing axes creation with gca
2395-
ax = plt.gca(projection=prj)
2398+
with pytest.warns(
2399+
MatplotlibDeprecationWarning,
2400+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
2401+
ax = plt.gca(projection=prj)
23962402
assert type(ax) == mpl.axes._subplots.subplot_class_factory(PolarAxes)
2397-
ax_via_gca = plt.gca(projection=prj)
2403+
with pytest.warns(
2404+
MatplotlibDeprecationWarning,
2405+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
2406+
ax_via_gca = plt.gca(projection=prj)
23982407
assert ax_via_gca is ax
23992408
# try getting the axes given a different polar projection
2400-
with pytest.warns(UserWarning) as rec:
2409+
with pytest.warns(
2410+
MatplotlibDeprecationWarning,
2411+
match=r'Calling gca\(\) with keyword arguments is deprecated'), \
2412+
pytest.raises(
2413+
ValueError, match=r'arguments passed to gca\(\) did not match'):
24012414
ax_via_gca = plt.gca(projection=prj2)
2402-
assert len(rec) == 1
2403-
assert 'Requested projection is different' in str(rec[0].message)
2404-
assert ax_via_gca is not ax
2405-
assert ax.get_theta_offset() == 0
2406-
assert ax_via_gca.get_theta_offset() == np.pi
24072415
# try getting the axes given an == (not is) polar projection
2408-
with pytest.warns(UserWarning):
2416+
with pytest.warns(
2417+
MatplotlibDeprecationWarning,
2418+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
24092419
ax_via_gca = plt.gca(projection=prj3)
2410-
assert len(rec) == 1
2411-
assert 'Requested projection is different' in str(rec[0].message)
24122420
assert ax_via_gca is ax
24132421
plt.close()
24142422

‎lib/matplotlib/tests/test_figure.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_figure.py
+24-9Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import matplotlib as mpl
1010
from matplotlib import cbook, rcParams
11+
from matplotlib.cbook import MatplotlibDeprecationWarning
1112
from matplotlib.testing.decorators import image_comparison, check_figures_equal
1213
from matplotlib.axes import Axes
1314
from matplotlib.figure import Figure
@@ -154,30 +155,44 @@ def test_gca():
154155
assert fig.add_axes() is None
155156

156157
ax0 = fig.add_axes([0, 0, 1, 1])
157-
assert fig.gca(projection='rectilinear') is ax0
158+
with pytest.warns(
159+
MatplotlibDeprecationWarning,
160+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
161+
assert fig.gca(projection='rectilinear') is ax0
158162
assert fig.gca() is ax0
159163

160164
ax1 = fig.add_axes(rect=[0.1, 0.1, 0.8, 0.8])
161-
assert fig.gca(projection='rectilinear') is ax1
165+
with pytest.warns(
166+
MatplotlibDeprecationWarning,
167+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
168+
assert fig.gca(projection='rectilinear') is ax1
162169
assert fig.gca() is ax1
163170

164171
ax2 = fig.add_subplot(121, projection='polar')
165172
assert fig.gca() is ax2
166-
assert fig.gca(polar=True) is ax2
173+
with pytest.warns(
174+
MatplotlibDeprecationWarning,
175+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
176+
assert fig.gca(polar=True) is ax2
167177

168178
ax3 = fig.add_subplot(122)
169179
assert fig.gca() is ax3
170180

171181
# the final request for a polar axes will end up creating one
172182
# with a spec of 111.
173-
with pytest.warns(UserWarning):
174-
# Changing the projection will throw a warning
175-
assert fig.gca(polar=True) is not ax3
176-
assert fig.gca(polar=True) is not ax2
177-
assert fig.gca().get_subplotspec().get_geometry() == (1, 1, 0, 0)
183+
with pytest.warns(
184+
MatplotlibDeprecationWarning,
185+
match=r'Calling gca\(\) with keyword arguments is deprecated'), \
186+
pytest.raises(
187+
ValueError, match=r'arguments passed to gca\(\) did not match'):
188+
# Changing the projection will raise an exception
189+
fig.gca(polar=True)
178190

179191
fig.sca(ax1)
180-
assert fig.gca(projection='rectilinear') is ax1
192+
with pytest.warns(
193+
MatplotlibDeprecationWarning,
194+
match=r'Calling gca\(\) with keyword arguments is deprecated'):
195+
assert fig.gca(projection='rectilinear') is ax1
181196
assert fig.gca() is ax1
182197

183198

0 commit comments

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