diff --git a/doc/api/next_api_changes/deprecations/19153-LPS.rst b/doc/api/next_api_changes/deprecations/19153-LPS.rst new file mode 100644 index 000000000000..b3094e3163e9 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/19153-LPS.rst @@ -0,0 +1,5 @@ +``pyplot.gca()``, ``Figure.gca`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing keyword arguments to `.pyplot.gca` or `.figure.Figure.gca` will not be +supported in a future release. diff --git a/doc/users/next_whats_new/axes_kwargs_collision.rst b/doc/users/next_whats_new/axes_kwargs_collision.rst new file mode 100644 index 000000000000..35d425a87dcb --- /dev/null +++ b/doc/users/next_whats_new/axes_kwargs_collision.rst @@ -0,0 +1,21 @@ +Changes to behavior of Axes creation methods (``gca()``, ``add_axes()``, ``add_subplot()``) +------------------------------------------------------------------------------------------- + +The behavior of the functions to create new axes (`.pyplot.axes`, +`.pyplot.subplot`, `.figure.Figure.add_axes`, +`.figure.Figure.add_subplot`) has changed. In the past, these functions would +detect if you were attempting to create Axes with the same keyword arguments as +already-existing axes in the current figure, and if so, they would return the +existing Axes. Now, these functions will always create new Axes. A special +exception is `.pyplot.subplot`, which will reuse any existing subplot with a +matching subplot spec. However, if there is a subplot with a matching subplot +spec, then that subplot will be returned, even if the keyword arguments with +which it was created differ. + +Correspondingly, the behavior of the functions to get the current Axes +(`.pyplot.gca`, `.figure.Figure.gca`) has changed. In the past, these functions +accepted keyword arguments. If the keyword arguments matched an +already-existing Axes, then that Axes would be returned, otherwise new Axes +would be created with those keyword arguments. Now, the keyword arguments are +only considered if there are no axes at all in the current figure. In a future +release, these functions will not accept keyword arguments at all. diff --git a/lib/matplotlib/axes/_subplots.py b/lib/matplotlib/axes/_subplots.py index 96315bb4ae6c..5042e3bbd860 100644 --- a/lib/matplotlib/axes/_subplots.py +++ b/lib/matplotlib/axes/_subplots.py @@ -1,5 +1,4 @@ import functools -import uuid from matplotlib import _api, docstring import matplotlib.artist as martist @@ -142,16 +141,7 @@ def _make_twin_axes(self, *args, **kwargs): # which currently uses this internal API. if kwargs["sharex"] is not self and kwargs["sharey"] is not self: raise ValueError("Twinned Axes may share only one axis") - # The dance here with label is to force add_subplot() to create a new - # Axes (by passing in a label never seen before). Note that this does - # not affect plot reactivation by subplot() as twin axes can never be - # reactivated by subplot(). - sentinel = str(uuid.uuid4()) - real_label = kwargs.pop("label", sentinel) - twin = self.figure.add_subplot( - self.get_subplotspec(), *args, label=sentinel, **kwargs) - if real_label is not sentinel: - twin.set_label(real_label) + twin = self.figure.add_subplot(self.get_subplotspec(), *args, **kwargs) self.set_adjustable('datalim') twin.set_adjustable('datalim') self._twinned_axes.join(self, twin) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index acc1320c3c9c..2fd7c70506fd 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -606,6 +606,9 @@ def __len__(self): def __getitem__(self, ind): return self._elements[ind] + def as_list(self): + return list(self._elements) + def forward(self): """Move the position forward and return the current element.""" self._pos = min(self._pos + 1, len(self._elements) - 1) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 71be82de0374..6e8a7957e2d8 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -52,113 +52,6 @@ def _stale_figure_callback(self, val): self.figure.stale = val -class _AxesStack(cbook.Stack): - """ - Specialization of `.Stack`, to handle all tracking of `~.axes.Axes` in a - `.Figure`. - - This stack stores ``key, (ind, axes)`` pairs, where: - - * **key** is a hash of the args and kwargs used in generating the Axes. - * **ind** is a serial index tracking the order in which Axes were added. - - AxesStack is a callable; calling it returns the current Axes. - The `current_key_axes` method returns the current key and associated Axes. - """ - - def __init__(self): - super().__init__() - self._ind = 0 - - def as_list(self): - """ - Return a list of the Axes instances that have been added to the figure. - """ - ia_list = [a for k, a in self._elements] - ia_list.sort() - return [a for i, a in ia_list] - - def get(self, key): - """ - Return the Axes instance that was added with *key*. - If it is not present, return *None*. - """ - item = dict(self._elements).get(key) - if item is None: - return None - _api.warn_deprecated( - "2.1", - message="Adding an axes using the same arguments as a previous " - "axes currently reuses the earlier instance. In a future " - "version, a new instance will always be created and returned. " - "Meanwhile, this warning can be suppressed, and the future " - "behavior ensured, by passing a unique label to each axes " - "instance.") - return item[1] - - def _entry_from_axes(self, e): - ind, k = {a: (ind, k) for k, (ind, a) in self._elements}[e] - return (k, (ind, e)) - - def remove(self, a): - """Remove the Axes from the stack.""" - super().remove(self._entry_from_axes(a)) - - def bubble(self, a): - """ - Move the given Axes, which must already exist in the - stack, to the top. - """ - return super().bubble(self._entry_from_axes(a)) - - def add(self, key, a): - """ - Add Axes *a*, with key *key*, to the stack, and return the stack. - - If *key* is unhashable, replace it by a unique, arbitrary object. - - If *a* is already on the stack, don't add it again, but - return *None*. - """ - # All the error checking may be unnecessary; but this method - # is called so seldom that the overhead is negligible. - _api.check_isinstance(Axes, a=a) - try: - hash(key) - except TypeError: - key = object() - - a_existing = self.get(key) - if a_existing is not None: - super().remove((key, a_existing)) - _api.warn_external( - "key {!r} already existed; Axes is being replaced".format(key)) - # I don't think the above should ever happen. - - if a in self: - return None - self._ind += 1 - return super().push((key, (self._ind, a))) - - def current_key_axes(self): - """ - Return a tuple of ``(key, axes)`` for the active Axes. - - If no Axes exists on the stack, then returns ``(None, None)``. - """ - if not len(self._elements): - return self._default, self._default - else: - key, (index, axes) = self._elements[self._pos] - return key, axes - - def __call__(self): - return self.current_key_axes()[1] - - def __contains__(self, a): - return a in self.as_list() - - class SubplotParams: """ A class to hold the parameters for a subplot. @@ -249,7 +142,7 @@ def __init__(self): self.figure = self # list of child gridspecs for this figure self._gridspecs = [] - self._localaxes = _AxesStack() # keep track of axes at this level + self._localaxes = cbook.Stack() # keep track of axes at this level self.artists = [] self.lines = [] self.patches = [] @@ -634,15 +527,6 @@ def add_axes(self, *args, **kwargs): Notes ----- - If the figure already has an Axes with key (*args*, - *kwargs*) then it will simply make that Axes current and - return it. This behavior is deprecated. Meanwhile, if you do - not want this behavior (i.e., you want to force the creation of a - new Axes), you must use a unique set of args and kwargs. The Axes - *label* attribute has been exposed for this purpose: if you want - two Axes that are otherwise identical to be added to the figure, - make sure you give them unique labels. - In rare circumstances, `.add_axes` may be called with a single argument, an Axes instance already created in the present figure but not in the figure's list of Axes. @@ -661,8 +545,7 @@ def add_axes(self, *args, **kwargs): rect = l, b, w, h fig = plt.figure() - fig.add_axes(rect, label=label1) - fig.add_axes(rect, label=label2) + fig.add_axes(rect) fig.add_axes(rect, frameon=False, facecolor='g') fig.add_axes(rect, polar=True) ax = fig.add_axes(rect, projection='polar') @@ -683,14 +566,6 @@ def add_axes(self, *args, **kwargs): "add_axes() got multiple values for argument 'rect'") args = (kwargs.pop('rect'), ) - # shortcut the projection "key" modifications later on, if an axes - # with the exact args/kwargs exists, return it immediately. - key = self._make_key(*args, **kwargs) - ax = self._axstack.get(key) - if ax is not None: - self.sca(ax) - return ax - if isinstance(args[0], Axes): a = args[0] if a.get_figure() is not self: @@ -701,19 +576,12 @@ def add_axes(self, *args, **kwargs): if not np.isfinite(rect).all(): raise ValueError('all entries in rect must be finite ' 'not {}'.format(rect)) - projection_class, kwargs, key = \ - self._process_projection_requirements(*args, **kwargs) - - # check that an axes of this type doesn't already exist, if it - # does, set it as active and return it - ax = self._axstack.get(key) - if isinstance(ax, projection_class): - self.sca(ax) - return ax + projection_class, kwargs = self._process_projection_requirements( + *args, **kwargs) # create the new axes using the axes class given a = projection_class(self, rect, **kwargs) - return self._add_axes_internal(key, a) + return self._add_axes_internal(a) @docstring.dedent_interpd def add_subplot(self, *args, **kwargs): @@ -793,17 +661,6 @@ def add_subplot(self, *args, **kwargs): %(Axes_kwdoc)s - Notes - ----- - If the figure already has a subplot with key (*args*, - *kwargs*) then it will simply make that subplot current and - return it. This behavior is deprecated. Meanwhile, if you do - not want this behavior (i.e., you want to force the creation of a - new subplot), you must use a unique set of args and kwargs. The Axes - *label* attribute has been exposed for this purpose: if you want - two subplots that are otherwise identical to be added to the figure, - make sure you give them unique labels. - See Also -------- .Figure.add_axes @@ -840,10 +697,6 @@ def add_subplot(self, *args, **kwargs): if ax.get_figure() is not self: raise ValueError("The Subplot must have been created in " "the present figure") - # make a key for the subplot (which includes the axes object id - # in the hash) - key = self._make_key(*args, **kwargs) - else: if not args: args = (1, 1, 1) @@ -853,28 +706,15 @@ def add_subplot(self, *args, **kwargs): if (len(args) == 1 and isinstance(args[0], Integral) and 100 <= args[0] <= 999): args = tuple(map(int, str(args[0]))) - projection_class, kwargs, key = \ - self._process_projection_requirements(*args, **kwargs) - ax = self._axstack.get(key) # search axes with this key in stack - if ax is not None: - if isinstance(ax, projection_class): - # the axes already existed, so set it as active & return - self.sca(ax) - return ax - else: - # Undocumented convenience behavior: - # subplot(111); subplot(111, projection='polar') - # will replace the first with the second. - # Without this, add_subplot would be simpler and - # more similar to add_axes. - self._axstack.remove(ax) + projection_class, kwargs = self._process_projection_requirements( + *args, **kwargs) ax = subplot_class_factory(projection_class)(self, *args, **kwargs) - return self._add_axes_internal(key, ax) + return self._add_axes_internal(ax) - def _add_axes_internal(self, key, ax): + def _add_axes_internal(self, ax): """Private helper for `add_axes` and `add_subplot`.""" - self._axstack.add(key, ax) - self._localaxes.add(key, ax) + self._axstack.push(ax) + self._localaxes.push(ax) self.sca(ax) ax._remove_method = self.delaxes self.stale = True @@ -1595,39 +1435,20 @@ def gca(self, **kwargs): %(Axes_kwdoc)s """ - ckey, cax = self._axstack.current_key_axes() - # if there exists an axes on the stack see if it matches - # the desired axes configuration - if cax is not None: - - # if no kwargs are given just return the current axes - # this is a convenience for gca() on axes such as polar etc. - if not kwargs: - return cax - - # if the user has specified particular projection detail - # then build up a key which can represent this - else: - projection_class, _, key = \ - self._process_projection_requirements(**kwargs) - - # let the returned axes have any gridspec by removing it from - # the key - ckey = ckey[1:] - key = key[1:] - - # if the cax matches this key then return the axes, otherwise - # continue and a new axes will be created - if key == ckey and isinstance(cax, projection_class): - return cax - else: - _api.warn_external('Requested projection is different ' - 'from current axis projection, ' - 'creating new axis with requested ' - 'projection.') - - # no axes found, so create one which spans the figure - return self.add_subplot(1, 1, 1, **kwargs) + if kwargs: + _api.warn_deprecated( + "3.4", + message="Calling gca() with keyword arguments was deprecated " + "in Matplotlib %(since)s. Starting %(removal)s, gca() will " + "take no keyword arguments. The gca() function should only be " + "used to get the current axes, or if no axes exist, create " + "new axes with default keyword arguments. To create a new " + "axes with non-default arguments, use plt.axes() or " + "plt.subplot().") + if self._axstack.empty(): + return self.add_subplot(1, 1, 1, **kwargs) + else: + return self._axstack() def _gci(self): # Helper for `~matplotlib.pyplot.gci`. Do not use elsewhere. @@ -1647,10 +1468,9 @@ def _gci(self): ``gci`` (get current image). """ # Look first for an image in the current Axes: - cax = self._axstack.current_key_axes()[1] - if cax is None: + if self._axstack.empty(): return None - im = cax._gci() + im = self._axstack()._gci() if im is not None: return im @@ -1669,7 +1489,7 @@ def _process_projection_requirements( """ Handle the args/kwargs to add_axes/add_subplot/gca, returning:: - (axes_proj_class, proj_class_kwargs, proj_stack_key) + (axes_proj_class, proj_class_kwargs) which can be used for new Axes initialization/identification. """ @@ -1698,41 +1518,7 @@ def _process_projection_requirements( f"projection must be a string, None or implement a " f"_as_mpl_axes method, not {projection!r}") - # Make the key without projection kwargs, this is used as a unique - # lookup for axes instances - key = self._make_key(*args, **kwargs) - - return projection_class, kwargs, key - - def _make_key(self, *args, **kwargs): - """Make a hashable key out of args and kwargs.""" - - def fixitems(items): - # items may have arrays and lists in them, so convert them - # to tuples for the key - ret = [] - for k, v in items: - # some objects can define __getitem__ without being - # iterable and in those cases the conversion to tuples - # will fail. So instead of using the np.iterable(v) function - # we simply try and convert to a tuple, and proceed if not. - try: - v = tuple(v) - except Exception: - pass - ret.append((k, v)) - return tuple(ret) - - def fixlist(args): - ret = [] - for a in args: - if np.iterable(a): - a = tuple(a) - ret.append(a) - return tuple(ret) - - key = fixlist(args), fixitems(kwargs.items()) - return key + return projection_class, kwargs def get_default_bbox_extra_artists(self): bbox_artists = [artist for artist in self.get_children() @@ -2366,7 +2152,7 @@ def __init__(self, self.set_tight_layout(tight_layout) - self._axstack = _AxesStack() # track all figure axes and current axes + self._axstack = cbook.Stack() # track all figure axes and current axes self.clf() self._cachedRenderer = None diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index d512d1876ab0..1791c2507649 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -42,7 +42,7 @@ from matplotlib import docstring from matplotlib.backend_bases import FigureCanvasBase, MouseButton from matplotlib.figure import Figure, figaspect -from matplotlib.gridspec import GridSpec +from matplotlib.gridspec import GridSpec, SubplotSpec from matplotlib import rcParams, rcParamsDefault, get_backend, rcParamsOrig from matplotlib.rcsetup import interactive_bk as _interactive_bk from matplotlib.artist import Artist @@ -1037,11 +1037,11 @@ def axes(arg=None, **kwargs): # Creating a new axes with specified dimensions and some kwargs plt.axes((left, bottom, width, height), facecolor='w') """ - + fig = gcf() if arg is None: - return subplot(**kwargs) + return fig.add_subplot(**kwargs) else: - return gcf().add_axes(arg, **kwargs) + return fig.add_axes(arg, **kwargs) def delaxes(ax=None): @@ -1221,7 +1221,18 @@ def subplot(*args, **kwargs): "and/or 'nrows'. Did you intend to call subplots()?") fig = gcf() - ax = fig.add_subplot(*args, **kwargs) + + # First, search for an existing subplot with a matching spec. + key = SubplotSpec._from_subplot_args(fig, args) + ax = next( + (ax for ax in fig.axes + if hasattr(ax, 'get_subplotspec') and ax.get_subplotspec() == key), + None) + + # If no existing axes match, then create a new one. + if ax is None: + ax = fig.add_subplot(*args, **kwargs) + bbox = ax.bbox axes_to_delete = [] for other_ax in fig.axes: @@ -2409,10 +2420,13 @@ def polar(*args, **kwargs): """ # If an axis already exists, check if it has a polar projection if gcf().get_axes(): - if not isinstance(gca(), PolarAxes): - _api.warn_external('Trying to create polar plot on an axis ' + ax = gca() + if isinstance(ax, PolarAxes): + return ax + else: + _api.warn_external('Trying to create polar plot on an Axes ' 'that does not have a polar projection.') - ax = gca(polar=True) + ax = axes(polar=True) ret = ax.plot(*args, **kwargs) return ret diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 2d448dd86d1a..948c3449e83f 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -2398,28 +2398,36 @@ def _as_mpl_axes(self): # testing axes creation with plt.axes ax = plt.axes([0, 0, 1, 1], projection=prj) assert type(ax) == PolarAxes - ax_via_gca = plt.gca(projection=prj) + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + ax_via_gca = plt.gca(projection=prj) assert ax_via_gca is ax plt.close() # testing axes creation with gca - ax = plt.gca(projection=prj) + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + ax = plt.gca(projection=prj) assert type(ax) == mpl.axes._subplots.subplot_class_factory(PolarAxes) - ax_via_gca = plt.gca(projection=prj) + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + ax_via_gca = plt.gca(projection=prj) assert ax_via_gca is ax # try getting the axes given a different polar projection - with pytest.warns(UserWarning) as rec: + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): ax_via_gca = plt.gca(projection=prj2) - assert len(rec) == 1 - assert 'Requested projection is different' in str(rec[0].message) - assert ax_via_gca is not ax + assert ax_via_gca is ax assert ax.get_theta_offset() == 0 - assert ax_via_gca.get_theta_offset() == np.pi # try getting the axes given an == (not is) polar projection - with pytest.warns(UserWarning): + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): ax_via_gca = plt.gca(projection=prj3) - assert len(rec) == 1 - assert 'Requested projection is different' in str(rec[0].message) assert ax_via_gca is ax plt.close() diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index c5ab3cf6d232..b5748491bdcf 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -15,6 +15,7 @@ import matplotlib.pyplot as plt import matplotlib.dates as mdates import matplotlib.gridspec as gridspec +from matplotlib.cbook import MatplotlibDeprecationWarning import numpy as np import pytest @@ -154,30 +155,44 @@ def test_gca(): assert fig.add_axes() is None ax0 = fig.add_axes([0, 0, 1, 1]) - assert fig.gca(projection='rectilinear') is ax0 + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + assert fig.gca(projection='rectilinear') is ax0 assert fig.gca() is ax0 ax1 = fig.add_axes(rect=[0.1, 0.1, 0.8, 0.8]) - assert fig.gca(projection='rectilinear') is ax1 + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + assert fig.gca(projection='rectilinear') is ax1 assert fig.gca() is ax1 ax2 = fig.add_subplot(121, projection='polar') assert fig.gca() is ax2 - assert fig.gca(polar=True) is ax2 + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + assert fig.gca(polar=True) is ax2 ax3 = fig.add_subplot(122) assert fig.gca() is ax3 - # the final request for a polar axes will end up creating one - # with a spec of 111. - with pytest.warns(UserWarning): - # Changing the projection will throw a warning - assert fig.gca(polar=True) is not ax3 - assert fig.gca(polar=True) is not ax2 - assert fig.gca().get_subplotspec().get_geometry() == (1, 1, 0, 0) + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + assert fig.gca(polar=True) is ax3 + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + assert fig.gca(polar=True) is not ax2 + assert fig.gca().get_subplotspec().get_geometry() == (1, 2, 1, 1) fig.sca(ax1) - assert fig.gca(projection='rectilinear') is ax1 + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + assert fig.gca(projection='rectilinear') is ax1 assert fig.gca() is ax1 @@ -922,3 +937,121 @@ def test_subfigure_double(): subfigsnest[1].supylabel('supylabel') axsRight = subfigs[1].subplots(2, 2) + + +def test_axes_kwargs(): + # plt.axes() always creates new axes, even if axes kwargs differ. + plt.figure() + ax = plt.axes() + ax1 = plt.axes() + assert ax is not None + assert ax1 is not ax + plt.close() + + plt.figure() + ax = plt.axes(projection='polar') + ax1 = plt.axes(projection='polar') + assert ax is not None + assert ax1 is not ax + plt.close() + + plt.figure() + ax = plt.axes(projection='polar') + ax1 = plt.axes() + assert ax is not None + assert ax1.name == 'rectilinear' + assert ax1 is not ax + plt.close() + + # fig.add_axes() always creates new axes, even if axes kwargs differ. + fig = plt.figure() + ax = fig.add_axes([0, 0, 1, 1]) + ax1 = fig.add_axes([0, 0, 1, 1]) + assert ax is not None + assert ax1 is not ax + plt.close() + + fig = plt.figure() + ax = fig.add_axes([0, 0, 1, 1], projection='polar') + ax1 = fig.add_axes([0, 0, 1, 1], projection='polar') + assert ax is not None + assert ax1 is not ax + plt.close() + + fig = plt.figure() + ax = fig.add_axes([0, 0, 1, 1], projection='polar') + ax1 = fig.add_axes([0, 0, 1, 1]) + assert ax is not None + assert ax1.name == 'rectilinear' + assert ax1 is not ax + plt.close() + + # fig.add_subplot() always creates new axes, even if axes kwargs differ. + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax1 = fig.add_subplot(1, 1, 1) + assert ax is not None + assert ax1 is not ax + plt.close() + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1, projection='polar') + ax1 = fig.add_subplot(1, 1, 1, projection='polar') + assert ax is not None + assert ax1 is not ax + plt.close() + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1, projection='polar') + ax1 = fig.add_subplot(1, 1, 1) + assert ax is not None + assert ax1.name == 'rectilinear' + assert ax1 is not ax + plt.close() + + # plt.subplot() searches for axes with the same subplot spec, and if one + # exists, returns it, regardless of whether the axes kwargs were the same. + fig = plt.figure() + ax = plt.subplot(1, 2, 1) + ax1 = plt.subplot(1, 2, 1) + ax2 = plt.subplot(1, 2, 2) + ax3 = plt.subplot(1, 2, 1, projection='polar') + assert ax is not None + assert ax1 is ax + assert ax2 is not ax + assert ax3 is ax + assert ax.name == 'rectilinear' + assert ax3.name == 'rectilinear' + plt.close() + + # plt.gca() returns an existing axes, unless there were no axes. + plt.figure() + ax = plt.gca() + ax1 = plt.gca() + assert ax is not None + assert ax1 is ax + plt.close() + + # plt.gca() raises a DeprecationWarning if called with kwargs. + plt.figure() + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + ax = plt.gca(projection='polar') + ax1 = plt.gca() + assert ax is not None + assert ax1 is ax + assert ax1.name == 'polar' + plt.close() + + # plt.gca() ignores keyword arguments if an axes already exists. + plt.figure() + ax = plt.gca() + with pytest.warns( + MatplotlibDeprecationWarning, + match=r'Calling gca\(\) with keyword arguments was deprecated'): + ax1 = plt.gca(projection='polar') + assert ax is not None + assert ax1 is ax + assert ax1.name == 'rectilinear' + plt.close() diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index 20316c82a8c1..89c3ba63acd0 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -899,7 +899,7 @@ def test_autoscale(): @pytest.mark.parametrize('auto', (True, False, None)) def test_unautoscale(axis, auto): fig = plt.figure() - ax = fig.gca(projection='3d') + ax = fig.add_subplot(projection='3d') x = np.arange(100) y = np.linspace(-0.1, 0.1, 100)