From da2f54996c8b567183ac238f867c43d955820b0f Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 24 Sep 2024 04:45:37 -0400 Subject: [PATCH 1/4] Remove 3.8 deprecations in toolkits --- .../next_api_changes/removals/28874-ES.rst | 33 ++++++++ .../api_changes_3.6.0/behaviour.rst | 2 +- .../api_changes_3.7.0/deprecations.rst | 2 +- .../api_changes_3.9.0/removals.rst | 2 +- doc/api/toolkits/axisartist.rst | 2 - .../axes_grid1/anchored_artists.py | 54 +------------ lib/mpl_toolkits/axes_grid1/axes_divider.py | 76 ------------------ lib/mpl_toolkits/axes_grid1/axes_grid.py | 5 -- lib/mpl_toolkits/axes_grid1/inset_locator.py | 47 +---------- .../test_axes_grid1/insetposition.png | Bin 1387 -> 0 bytes .../axes_grid1/tests/test_axes_grid1.py | 30 +++---- lib/mpl_toolkits/axisartist/axes_divider.py | 2 +- lib/mpl_toolkits/axisartist/axes_grid.py | 23 ------ lib/mpl_toolkits/axisartist/axes_rgb.py | 18 ----- lib/mpl_toolkits/axisartist/axislines.py | 4 - lib/mpl_toolkits/axisartist/floating_axes.py | 11 --- lib/mpl_toolkits/axisartist/meson.build | 2 - lib/mpl_toolkits/mplot3d/axis3d.py | 10 --- 18 files changed, 51 insertions(+), 272 deletions(-) create mode 100644 doc/api/next_api_changes/removals/28874-ES.rst delete mode 100644 lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/insetposition.png delete mode 100644 lib/mpl_toolkits/axisartist/axes_grid.py delete mode 100644 lib/mpl_toolkits/axisartist/axes_rgb.py diff --git a/doc/api/next_api_changes/removals/28874-ES.rst b/doc/api/next_api_changes/removals/28874-ES.rst new file mode 100644 index 000000000000..32c9ecb1ceaf --- /dev/null +++ b/doc/api/next_api_changes/removals/28874-ES.rst @@ -0,0 +1,33 @@ +``axes_grid1`` API changes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``anchored_artists.AnchoredEllipse`` has been removed. Instead, directly construct an +`.AnchoredOffsetbox`, an `.AuxTransformBox`, and an `~.patches.Ellipse`, as demonstrated +in :doc:`/gallery/misc/anchored_artists`. + +The ``axes_divider.AxesLocator`` class has been removed. The ``new_locator`` method of +divider instances now instead returns an opaque callable (which can still be passed to +``ax.set_axes_locator``). + +``axes_divider.Divider.locate`` has been removed; use ``Divider.new_locator(...)(ax, +renderer)`` instead. + +``axes_grid.CbarAxesBase.toggle_label`` has been removed. Instead, use standard methods +for manipulating colorbar labels (`.Colorbar.set_label`) and tick labels +(`.Axes.tick_params`). + +``inset_location.InsetPosition`` has been removed; use `~.Axes.inset_axes` instead. + + +``axisartist`` API changes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``axisartist.axes_grid`` and ``axisartist.axes_rgb`` modules, which provide wrappers +combining the functionality of `.axes_grid1` and `.axisartist`, have been removed; +directly use e.g. ``AxesGrid(..., axes_class=axislines.Axes)`` instead. + +Calling an axisartist Axes to mean `~matplotlib.pyplot.axis` has been removed; explicitly +call the method instead. + +``floating_axes.GridHelperCurveLinear.get_data_boundary`` has been removed. Use +``grid_finder.extreme_finder(*[None] * 5)`` to get the extremes of the grid. diff --git a/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst index 91802692ebb4..6ace010515fb 100644 --- a/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst +++ b/doc/api/prev_api_changes/api_changes_3.6.0/behaviour.rst @@ -241,7 +241,7 @@ Specified exception types in ``Grid`` In a few cases an `Exception` was thrown when an incorrect argument value was set in the `mpl_toolkits.axes_grid1.axes_grid.Grid` (= -`mpl_toolkits.axisartist.axes_grid.Grid`) constructor. These are replaced as +``mpl_toolkits.axisartist.axes_grid.Grid``) constructor. These are replaced as follows: * Providing an incorrect value for *ngrids* now raises a `ValueError` diff --git a/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst b/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst index dd6d9d8e0894..55a0a7133c65 100644 --- a/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst +++ b/doc/api/prev_api_changes/api_changes_3.7.0/deprecations.rst @@ -90,7 +90,7 @@ Passing undefined *label_mode* to ``Grid`` ... is deprecated. This includes `mpl_toolkits.axes_grid1.axes_grid.Grid`, `mpl_toolkits.axes_grid1.axes_grid.AxesGrid`, and `mpl_toolkits.axes_grid1.axes_grid.ImageGrid` as well as the corresponding -classes imported from `mpl_toolkits.axisartist.axes_grid`. +classes imported from ``mpl_toolkits.axisartist.axes_grid``. Pass ``label_mode='keep'`` instead to get the previous behavior of not modifying labels. diff --git a/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst b/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst index b9aa03cfbf92..791c04149981 100644 --- a/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst +++ b/doc/api/prev_api_changes/api_changes_3.9.0/removals.rst @@ -111,7 +111,7 @@ Passing undefined *label_mode* to ``Grid`` ... is no longer allowed. This includes `mpl_toolkits.axes_grid1.axes_grid.Grid`, `mpl_toolkits.axes_grid1.axes_grid.AxesGrid`, and `mpl_toolkits.axes_grid1.axes_grid.ImageGrid` as well as the corresponding classes -imported from `mpl_toolkits.axisartist.axes_grid`. +imported from ``mpl_toolkits.axisartist.axes_grid``. Pass ``label_mode='keep'`` instead to get the previous behavior of not modifying labels. diff --git a/doc/api/toolkits/axisartist.rst b/doc/api/toolkits/axisartist.rst index 8cac4d68a266..5f58d134d370 100644 --- a/doc/api/toolkits/axisartist.rst +++ b/doc/api/toolkits/axisartist.rst @@ -34,8 +34,6 @@ You can find a tutorial describing usage of axisartist at the axisartist.angle_helper axisartist.axes_divider - axisartist.axes_grid - axisartist.axes_rgb axisartist.axis_artist axisartist.axisline_style axisartist.axislines diff --git a/lib/mpl_toolkits/axes_grid1/anchored_artists.py b/lib/mpl_toolkits/axes_grid1/anchored_artists.py index 1238310b462b..214b15843ebf 100644 --- a/lib/mpl_toolkits/axes_grid1/anchored_artists.py +++ b/lib/mpl_toolkits/axes_grid1/anchored_artists.py @@ -1,12 +1,12 @@ -from matplotlib import _api, transforms +from matplotlib import transforms from matplotlib.offsetbox import (AnchoredOffsetbox, AuxTransformBox, DrawingArea, TextArea, VPacker) -from matplotlib.patches import (Rectangle, Ellipse, ArrowStyle, +from matplotlib.patches import (Rectangle, ArrowStyle, FancyArrowPatch, PathPatch) from matplotlib.text import TextPath __all__ = ['AnchoredDrawingArea', 'AnchoredAuxTransformBox', - 'AnchoredEllipse', 'AnchoredSizeBar', 'AnchoredDirectionArrows'] + 'AnchoredSizeBar', 'AnchoredDirectionArrows'] class AnchoredDrawingArea(AnchoredOffsetbox): @@ -124,54 +124,6 @@ def __init__(self, transform, loc, **kwargs) -@_api.deprecated("3.8") -class AnchoredEllipse(AnchoredOffsetbox): - def __init__(self, transform, width, height, angle, loc, - pad=0.1, borderpad=0.1, prop=None, frameon=True, **kwargs): - """ - Draw an anchored ellipse of a given size. - - Parameters - ---------- - transform : `~matplotlib.transforms.Transform` - The transformation object for the coordinate system in use, i.e., - :attr:`matplotlib.axes.Axes.transData`. - width, height : float - Width and height of the ellipse, given in coordinates of - *transform*. - angle : float - Rotation of the ellipse, in degrees, anti-clockwise. - loc : str - Location of the ellipse. Valid locations are - 'upper left', 'upper center', 'upper right', - 'center left', 'center', 'center right', - 'lower left', 'lower center', 'lower right'. - For backward compatibility, numeric values are accepted as well. - See the parameter *loc* of `.Legend` for details. - pad : float, default: 0.1 - Padding around the ellipse, in fraction of the font size. - borderpad : float, default: 0.1 - Border padding, in fraction of the font size. - frameon : bool, default: True - If True, draw a box around the ellipse. - prop : `~matplotlib.font_manager.FontProperties`, optional - Font property used as a reference for paddings. - **kwargs - Keyword arguments forwarded to `.AnchoredOffsetbox`. - - Attributes - ---------- - ellipse : `~matplotlib.patches.Ellipse` - Ellipse patch drawn. - """ - self._box = AuxTransformBox(transform) - self.ellipse = Ellipse((0, 0), width, height, angle=angle) - self._box.add_artist(self.ellipse) - - super().__init__(loc, pad=pad, borderpad=borderpad, child=self._box, - prop=prop, frameon=frameon, **kwargs) - - class AnchoredSizeBar(AnchoredOffsetbox): def __init__(self, transform, size, label, loc, pad=0.1, borderpad=0.1, sep=2, diff --git a/lib/mpl_toolkits/axes_grid1/axes_divider.py b/lib/mpl_toolkits/axes_grid1/axes_divider.py index f6c38f35dbc4..50365f482b72 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_divider.py +++ b/lib/mpl_toolkits/axes_grid1/axes_divider.py @@ -199,31 +199,6 @@ def new_locator(self, nx, ny, nx1=None, ny1=None): locator.get_subplotspec = self.get_subplotspec return locator - @_api.deprecated( - "3.8", alternative="divider.new_locator(...)(ax, renderer)") - def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): - """ - Implementation of ``divider.new_locator().__call__``. - - Parameters - ---------- - nx, nx1 : int - Integers specifying the column-position of the cell. When *nx1* is - None, a single *nx*-th column is specified. Otherwise, the - location of columns spanning between *nx* to *nx1* (but excluding - *nx1*-th column) is specified. - ny, ny1 : int - Same as *nx* and *nx1*, but for row positions. - axes - renderer - """ - xref = self._xrefindex - yref = self._yrefindex - return self._locate( - nx - xref, (nx + 1 if nx1 is None else nx1) - xref, - ny - yref, (ny + 1 if ny1 is None else ny1) - yref, - axes, renderer) - def _locate(self, nx, ny, nx1, ny1, axes, renderer): """ Implementation of ``divider.new_locator().__call__``. @@ -305,57 +280,6 @@ def add_auto_adjustable_area(self, use_axes, pad=0.1, adjust_dirs=None): self.append_size(d, Size._AxesDecorationsSize(use_axes, d) + pad) -@_api.deprecated("3.8") -class AxesLocator: - """ - A callable object which returns the position and size of a given - `.AxesDivider` cell. - """ - - def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None): - """ - Parameters - ---------- - axes_divider : `~mpl_toolkits.axes_grid1.axes_divider.AxesDivider` - nx, nx1 : int - Integers specifying the column-position of the - cell. When *nx1* is None, a single *nx*-th column is - specified. Otherwise, location of columns spanning between *nx* - to *nx1* (but excluding *nx1*-th column) is specified. - ny, ny1 : int - Same as *nx* and *nx1*, but for row positions. - """ - self._axes_divider = axes_divider - - _xrefindex = axes_divider._xrefindex - _yrefindex = axes_divider._yrefindex - - self._nx, self._ny = nx - _xrefindex, ny - _yrefindex - - if nx1 is None: - nx1 = len(self._axes_divider) - if ny1 is None: - ny1 = len(self._axes_divider[0]) - - self._nx1 = nx1 - _xrefindex - self._ny1 = ny1 - _yrefindex - - def __call__(self, axes, renderer): - - _xrefindex = self._axes_divider._xrefindex - _yrefindex = self._axes_divider._yrefindex - - return self._axes_divider.locate(self._nx + _xrefindex, - self._ny + _yrefindex, - self._nx1 + _xrefindex, - self._ny1 + _yrefindex, - axes, - renderer) - - def get_subplotspec(self): - return self._axes_divider.get_subplotspec() - - class SubplotDivider(Divider): """ The Divider class whose rectangle area is specified as a subplot geometry. diff --git a/lib/mpl_toolkits/axes_grid1/axes_grid.py b/lib/mpl_toolkits/axes_grid1/axes_grid.py index b5663364481e..20abf18ea79c 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_grid.py +++ b/lib/mpl_toolkits/axes_grid1/axes_grid.py @@ -20,11 +20,6 @@ def colorbar(self, mappable, **kwargs): return self.get_figure(root=False).colorbar( mappable, cax=self, location=self.orientation, **kwargs) - @_api.deprecated("3.8", alternative="ax.tick_params and colorbar.set_label") - def toggle_label(self, b): - axis = self.axis[self.orientation] - axis.toggle(ticklabels=b, label=b) - _cbaraxes_class_factory = cbook._make_class_factory(CbarAxesBase, "Cbar{}") diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 303dbbb0721e..52fe6efc0618 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -6,58 +6,13 @@ from matplotlib.offsetbox import AnchoredOffsetbox from matplotlib.patches import Patch, Rectangle from matplotlib.path import Path -from matplotlib.transforms import Bbox, BboxTransformTo +from matplotlib.transforms import Bbox from matplotlib.transforms import IdentityTransform, TransformedBbox from . import axes_size as Size from .parasite_axes import HostAxes -@_api.deprecated("3.8", alternative="Axes.inset_axes") -class InsetPosition: - @_docstring.interpd - def __init__(self, parent, lbwh): - """ - An object for positioning an inset axes. - - This is created by specifying the normalized coordinates in the axes, - instead of the figure. - - Parameters - ---------- - parent : `~matplotlib.axes.Axes` - Axes to use for normalizing coordinates. - - lbwh : iterable of four floats - The left edge, bottom edge, width, and height of the inset axes, in - units of the normalized coordinate of the *parent* axes. - - See Also - -------- - :meth:`matplotlib.axes.Axes.set_axes_locator` - - Examples - -------- - The following bounds the inset axes to a box with 20%% of the parent - axes height and 40%% of the width. The size of the axes specified - ([0, 0, 1, 1]) ensures that the axes completely fills the bounding box: - - >>> parent_axes = plt.gca() - >>> ax_ins = plt.axes([0, 0, 1, 1]) - >>> ip = InsetPosition(parent_axes, [0.5, 0.1, 0.4, 0.2]) - >>> ax_ins.set_axes_locator(ip) - """ - self.parent = parent - self.lbwh = lbwh - - def __call__(self, ax, renderer): - bbox_parent = self.parent.get_position(original=False) - trans = BboxTransformTo(bbox_parent) - bbox_inset = Bbox.from_bounds(*self.lbwh) - bb = TransformedBbox(bbox_inset, trans) - return bb - - class AnchoredLocatorBase(AnchoredOffsetbox): def __init__(self, bbox_to_anchor, offsetbox, loc, borderpad=0.5, bbox_transform=None): diff --git a/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/insetposition.png b/lib/mpl_toolkits/axes_grid1/tests/baseline_images/test_axes_grid1/insetposition.png deleted file mode 100644 index e8676cfd6c9528f7e002710ca048d833b19f9b72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1387 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yg_pQSlmzFem6RtIr84*?mK5aV zm*iw7DU_ua6=&w>8S9zp8R(^?mKmFx8EL1RCYzXAni`pzC+npc85kKESQ?t@C}fnB z6ck(O>*vC?>*W`v>+fMYs06fvv%n*=n1O*m5ri36*e}myU|`wh>EaktG3V`F!@gTt z5^Wdt!?!P7l*pKuIoXIifp>AD@R}Cih*_o^I%ZD0#=T%>*QMje`z@UFktuHb+eYOyqRP6 z#X|1J$BH%UUT1BMx|+0c$2rg6jazeXhfh6RxOs8z?bUnh+5dB2GVu)!-I^PH`f1UN z!u>aL%(mXm%eTA!=Jxq}@4s)2Tfcw*^L2?I9Q?M2o_@N?uKu6RmmTqEo_`K}Hp}+h zn^n7Bt=+@_65>XKpAPfp%{x=K;O2ETfz9fszi+9@bHq3i<#ZgLY_`AqH)Zq9D>Zii zU#9!}`%kVqA+i1Qr%yrp`ug^N-~9di_f@go*V_B>f7jSoR9COokKcFY_3PIw`j4dl zYO7ek^Jo)sA%|vt-nSd`4Q2S+Z@n$MYT|n~ZSzL`=Xo;y$6xJQ7q&W7SFC%ZetO7c zzLf2^uU74q`)%|t?Dn>=fBx8fOYO^WzcCC%ln_mA%VdKvlyHnZ6=f#HRd7o}C>3~G`33k28Uw@YRuahq} zyU+22nW-}88~a1OyIJ)UX0vX(oA;Lg(A7Wh?5|(`r*Qp=7&jz=sO`P-Mn|s>ZBk&}m4+7a6b;4}UCmnsG$h0xyqfNJ{F>Vb_?Gaf?y4NbN zC8hEUc5ID!9@#zhrhLoplzIVN*UUhRnoo`Y z>shN-U+=S>ozL=#vC+m1YS6(Y(`Nxge$}d5@4wgA^nL&Sy{fu;Hz+)7f7Q;dtMAXb z)L0T59vb@k>#s$hV>ao%ykzQ|eD2+@b^Gf6R$a7wcXtVM6&=rPWhs*V=Dpzj=SJe8lZ-wV{?W=GM2i?Y;H8{KH>IzINxgW!BYGjd#d@FOSy| s3%7j!pz(n1XB02siF}aboBlH}1W9dw Date: Tue, 24 Sep 2024 22:17:04 -0400 Subject: [PATCH 2/4] Remove 3.8 deprecations in backends --- .../next_api_changes/removals/28874-ES.rst | 63 +++++++++++++++++++ lib/matplotlib/backend_bases.py | 38 +---------- lib/matplotlib/backend_bases.pyi | 5 +- lib/matplotlib/backends/backend_agg.py | 14 ----- lib/matplotlib/backends/backend_pdf.py | 22 +------ lib/matplotlib/backends/backend_pgf.py | 22 +------ lib/matplotlib/backends/backend_ps.py | 22 +------ lib/matplotlib/backends/backend_qt.py | 4 -- lib/matplotlib/pyplot.py | 8 --- lib/matplotlib/rcsetup.py | 17 +---- lib/matplotlib/tests/test_backend_pdf.py | 38 ++--------- lib/matplotlib/tests/test_backend_pgf.py | 38 ++--------- lib/matplotlib/tests/test_backend_ps.py | 6 +- lib/matplotlib/tests/test_pyplot.py | 5 +- 14 files changed, 88 insertions(+), 214 deletions(-) diff --git a/doc/api/next_api_changes/removals/28874-ES.rst b/doc/api/next_api_changes/removals/28874-ES.rst index 32c9ecb1ceaf..d5bdaeef6458 100644 --- a/doc/api/next_api_changes/removals/28874-ES.rst +++ b/doc/api/next_api_changes/removals/28874-ES.rst @@ -1,3 +1,66 @@ +Auto-closing of figures when switching backend +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Allowable backend switches (i.e. those that do not swap a GUI event loop with another +one) will not close existing figures. If necessary, call ``plt.close("all")`` before +switching. + + +``FigureCanvasBase.switch_backends`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed with no replacement. + + +Accessing ``event.guiEvent`` after event handlers return +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... is no longer supported, and ``event.guiEvent`` will be set to None once the event +handlers return. For some GUI toolkits, it is unsafe to use the event, though you may +separately stash the object at your own risk. + + +``PdfPages(keep_empty=True)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A zero-page PDF is not valid, thus passing ``keep_empty=True`` to `.backend_pdf.PdfPages` +and `.backend_pgf.PdfPages`, and the ``keep_empty`` attribute of these classes, is no +longer allowed, and empty PDF files will not be created. + +Furthermore, `.backend_pdf.PdfPages` no longer immediately creates the target file upon +instantiation, but only when the first figure is saved. To fully control file creation, +directly pass an opened file object as argument (``with open(path, "wb") as file, +PdfPages(file) as pdf: ...``). + + +``backend_ps.psDefs`` +~~~~~~~~~~~~~~~~~~~~~ + +The ``psDefs`` module-level variable in ``backend_ps`` has been removed with no +replacement. + + +Automatic papersize selection in PostScript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Setting :rc:`ps.papersize` to ``'auto'`` or passing ``papersize='auto'`` to +`.Figure.savefig` is no longer supported. Either pass an explicit paper type name, or +omit this parameter to use the default from the rcParam. + + +``RendererAgg.tostring_rgb`` and ``FigureCanvasAgg.tostring_rgb`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... have been remove with no direct replacement. Consider using ``buffer_rgba`` instead, +which should cover most use cases. + + +``NavigationToolbar2QT.message`` has been removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... with no replacement. + + ``axes_grid1`` API changes ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 817eb51705fe..95ed49612b35 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1178,26 +1178,12 @@ class Event: def __init__(self, name, canvas, guiEvent=None): self.name = name self.canvas = canvas - self._guiEvent = guiEvent - self._guiEvent_deleted = False + self.guiEvent = guiEvent def _process(self): """Process this event on ``self.canvas``, then unset ``guiEvent``.""" self.canvas.callbacks.process(self.name, self) - self._guiEvent_deleted = True - - @property - def guiEvent(self): - # After deprecation elapses: remove _guiEvent_deleted; make guiEvent a plain - # attribute set to None by _process. - if self._guiEvent_deleted: - _api.warn_deprecated( - "3.8", message="Accessing guiEvent outside of the original GUI event " - "handler is unsafe and deprecated since %(since)s; in the future, the " - "attribute will be set to None after quitting the event handler. You " - "may separately record the value of the guiEvent attribute at your own " - "risk.") - return self._guiEvent + self.guiEvent = None class DrawEvent(Event): @@ -2097,12 +2083,6 @@ def print_figure( if dpi == 'figure': dpi = getattr(self.figure, '_original_dpi', self.figure.dpi) - if kwargs.get("papertype") == 'auto': - # When deprecation elapses, remove backend_ps._get_papertype & its callers. - _api.warn_deprecated( - "3.8", name="papertype='auto'", addendum="Pass an explicit paper type, " - "'figure', or omit the *papertype* argument entirely.") - # Remove the figure manager, if any, to avoid resizing the GUI widget. with (cbook._setattr_cm(self, manager=None), self._switch_canvas_and_return_print_method(format, backend) @@ -2207,20 +2187,6 @@ def get_default_filename(self): default_filetype = self.get_default_filetype() return f'{default_basename}.{default_filetype}' - @_api.deprecated("3.8") - def switch_backends(self, FigureCanvasClass): - """ - Instantiate an instance of FigureCanvasClass - - This is used for backend switching, e.g., to instantiate a - FigureCanvasPS from a FigureCanvasGTK. Note, deep copying is - not done, so any changes to one of the instances (e.g., setting - figure size or line props), will be reflected in the other - """ - newCanvas = FigureCanvasClass(self.figure) - newCanvas._is_saving = self._is_saving - return newCanvas - def mpl_connect(self, s, func): """ Bind function *func* to event *s*. diff --git a/lib/matplotlib/backend_bases.pyi b/lib/matplotlib/backend_bases.pyi index c2fc61e386d8..70be504666fc 100644 --- a/lib/matplotlib/backend_bases.pyi +++ b/lib/matplotlib/backend_bases.pyi @@ -199,13 +199,11 @@ class TimerBase: class Event: name: str canvas: FigureCanvasBase + guiEvent: Any def __init__( self, name: str, canvas: FigureCanvasBase, guiEvent: Any | None = ... ) -> None: ... - @property - def guiEvent(self) -> Any: ... - class DrawEvent(Event): renderer: RendererBase def __init__( @@ -348,7 +346,6 @@ class FigureCanvasBase: def get_default_filetype(cls) -> str: ... def get_default_filename(self) -> str: ... _T = TypeVar("_T", bound=FigureCanvasBase) - def switch_backends(self, FigureCanvasClass: type[_T]) -> _T: ... def mpl_connect(self, s: str, func: Callable[[Event], Any]) -> int: ... def mpl_disconnect(self, cid: int) -> None: ... def new_timer( diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 92253c02c1b5..ae361f0cceb4 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -266,10 +266,6 @@ def buffer_rgba(self): def tostring_argb(self): return np.asarray(self._renderer).take([3, 0, 1, 2], axis=2).tobytes() - @_api.deprecated("3.8", alternative="buffer_rgba") - def tostring_rgb(self): - return np.asarray(self._renderer).take([0, 1, 2], axis=2).tobytes() - def clear(self): self._renderer.clear() @@ -398,16 +394,6 @@ def get_renderer(self): self._lastKey = key return self.renderer - @_api.deprecated("3.8", alternative="buffer_rgba") - def tostring_rgb(self): - """ - Get the image as RGB `bytes`. - - `draw` must be called at least once before this function will work and - to update the renderer for any subsequent changes to the Figure. - """ - return self.renderer.tostring_rgb() - def tostring_argb(self): """ Get the image as ARGB `bytes`. diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 7e3e09f034f5..9c542e2a8f8e 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -2663,9 +2663,9 @@ class PdfPages: confusion when using `~.pyplot.savefig` and forgetting the format argument. """ - _UNSET = object() - - def __init__(self, filename, keep_empty=_UNSET, metadata=None): + @_api.delete_parameter("3.10", "keep_empty", + addendum="This parameter does nothing.") + def __init__(self, filename, keep_empty=None, metadata=None): """ Create a new PdfPages object. @@ -2676,10 +2676,6 @@ def __init__(self, filename, keep_empty=_UNSET, metadata=None): The file is opened when a figure is saved for the first time (overwriting any older file with the same name). - keep_empty : bool, optional - If set to False, then empty pdf files will be deleted automatically - when closed. - metadata : dict, optional Information dictionary object (see PDF reference section 10.2.1 'Document Information Dictionary'), e.g.: @@ -2693,13 +2689,6 @@ def __init__(self, filename, keep_empty=_UNSET, metadata=None): self._filename = filename self._metadata = metadata self._file = None - if keep_empty and keep_empty is not self._UNSET: - _api.warn_deprecated("3.8", message=( - "Keeping empty pdf files is deprecated since %(since)s and support " - "will be removed %(removal)s.")) - self._keep_empty = keep_empty - - keep_empty = _api.deprecate_privatize_attribute("3.8") def __enter__(self): return self @@ -2721,11 +2710,6 @@ def close(self): self._file.finalize() self._file.close() self._file = None - elif self._keep_empty: # True *or* UNSET. - _api.warn_deprecated("3.8", message=( - "Keeping empty pdf files is deprecated since %(since)s and support " - "will be removed %(removal)s.")) - PdfFile(self._filename, metadata=self._metadata).close() # touch the file. def infodict(self): """ diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index daefdb0640ca..48b6e8ac152c 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -14,7 +14,7 @@ from PIL import Image import matplotlib as mpl -from matplotlib import _api, cbook, font_manager as fm +from matplotlib import cbook, font_manager as fm from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, RendererBase ) @@ -898,9 +898,7 @@ class PdfPages: ... pdf.savefig() """ - _UNSET = object() - - def __init__(self, filename, *, keep_empty=_UNSET, metadata=None): + def __init__(self, filename, *, metadata=None): """ Create a new PdfPages object. @@ -910,10 +908,6 @@ def __init__(self, filename, *, keep_empty=_UNSET, metadata=None): Plots using `PdfPages.savefig` will be written to a file at this location. Any older file with the same name is overwritten. - keep_empty : bool, default: True - If set to False, then empty pdf files will be deleted automatically - when closed. - metadata : dict, optional Information dictionary object (see PDF reference section 10.2.1 'Document Information Dictionary'), e.g.: @@ -929,17 +923,10 @@ def __init__(self, filename, *, keep_empty=_UNSET, metadata=None): """ self._output_name = filename self._n_figures = 0 - if keep_empty and keep_empty is not self._UNSET: - _api.warn_deprecated("3.8", message=( - "Keeping empty pdf files is deprecated since %(since)s and support " - "will be removed %(removal)s.")) - self._keep_empty = keep_empty self._metadata = (metadata or {}).copy() self._info_dict = _create_pdf_info_dict('pgf', self._metadata) self._file = BytesIO() - keep_empty = _api.deprecate_privatize_attribute("3.8") - def _write_header(self, width_inches, height_inches): pdfinfo = ','.join( _metadata_to_str(k, v) for k, v in self._info_dict.items()) @@ -969,11 +956,6 @@ def close(self): self._file.write(rb'\end{document}\n') if self._n_figures > 0: self._run_latex() - elif self._keep_empty: - _api.warn_deprecated("3.8", message=( - "Keeping empty pdf files is deprecated since %(since)s and support " - "will be removed %(removal)s.")) - open(self._output_name, 'wb').close() self._file.close() def _run_latex(self): diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 5f224f38af1e..4f4c27cce955 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -39,12 +39,6 @@ debugPS = False -@_api.caching_module_getattr -class __getattr__: - # module-level deprecations - psDefs = _api.deprecated("3.8", obj_type="")(property(lambda self: _psDefs)) - - papersize = {'letter': (8.5, 11), 'legal': (8.5, 14), 'ledger': (11, 17), @@ -72,15 +66,6 @@ class __getattr__: 'b10': (1.26, 1.76)} -def _get_papertype(w, h): - for key, (pw, ph) in sorted(papersize.items(), reverse=True): - if key.startswith('l'): - continue - if w < pw and h < ph: - return key - return 'a0' - - def _nums_to_str(*args, sep=" "): return sep.join(f"{arg:1.3f}".rstrip("0").rstrip(".") for arg in args) @@ -828,7 +813,7 @@ def _print_ps( if papertype is None: papertype = mpl.rcParams['ps.papersize'] papertype = papertype.lower() - _api.check_in_list(['figure', 'auto', *papersize], papertype=papertype) + _api.check_in_list(['figure', *papersize], papertype=papertype) orientation = _api.check_getitem( _Orientation, orientation=orientation.lower()) @@ -858,9 +843,6 @@ def _print_figure( # find the appropriate papertype width, height = self.figure.get_size_inches() - if papertype == 'auto': - papertype = _get_papertype(*orientation.swap_if_landscape((width, height))) - if is_eps or papertype == 'figure': paper_width, paper_height = width, height else: @@ -1041,8 +1023,6 @@ def _print_figure_tex( paper_width, paper_height = orientation.swap_if_landscape( self.figure.get_size_inches()) else: - if papertype == 'auto': - papertype = _get_papertype(width, height) paper_width, paper_height = papersize[papertype] psfrag_rotated = _convert_psfrags( diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py index e693811df4f0..bc37a15c7a67 100644 --- a/lib/matplotlib/backends/backend_qt.py +++ b/lib/matplotlib/backends/backend_qt.py @@ -658,9 +658,6 @@ def set_window_title(self, title): class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar): - _message = QtCore.Signal(str) # Remove once deprecation below elapses. - message = _api.deprecate_privatize_attribute("3.8") - toolitems = [*NavigationToolbar2.toolitems] toolitems.insert( # Add 'customize' action after 'subplots' @@ -783,7 +780,6 @@ def zoom(self, *args): self._update_buttons_checked() def set_message(self, s): - self._message.emit(s) if self.coordinates: self.locLabel.setText(s) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 744eee0e4b9f..69c80e6d3579 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -514,14 +514,6 @@ def draw_if_interactive() -> None: # See https://github.com/matplotlib/matplotlib/issues/6092 matplotlib.backends.backend = newbackend # type: ignore[attr-defined] - if not cbook._str_equal(old_backend, newbackend): - if get_fignums(): - _api.warn_deprecated("3.8", message=( - "Auto-close()ing of figures upon backend switching is deprecated since " - "%(since)s and will be removed %(removal)s. To suppress this warning, " - "explicitly call plt.close('all') first.")) - close("all") - # Make sure the repl display hook is installed in case we become interactive. install_repl_displayhook() diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index e84b0539385b..308e02fca72b 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -463,19 +463,6 @@ def validate_ps_distiller(s): return ValidateInStrings('ps.usedistiller', ['ghostscript', 'xpdf'])(s) -def _validate_papersize(s): - # Re-inline this validator when the 'auto' deprecation expires. - s = ValidateInStrings("ps.papersize", - ["figure", "auto", "letter", "legal", "ledger", - *[f"{ab}{i}" for ab in "ab" for i in range(11)]], - ignorecase=True)(s) - if s == "auto": - _api.warn_deprecated("3.8", name="ps.papersize='auto'", - addendum="Pass an explicit paper type, figure, or omit " - "the *ps.papersize* rcParam entirely.") - return s - - # A validator dedicated to the named line styles, based on the items in # ls_mapper, and a list of possible strings read from Line2D.set_linestyle _validate_named_linestyle = ValidateInStrings( @@ -1291,7 +1278,9 @@ def _convert_validator_spec(key, conv): "tk.window_focus": validate_bool, # Maintain shell focus for TkAgg # Set the papersize/type - "ps.papersize": _validate_papersize, + "ps.papersize": _ignorecase( + ["figure", "letter", "legal", "ledger", + *[f"{ab}{i}" for ab in "ab" for i in range(11)]]), "ps.useafm": validate_bool, # use ghostscript or xpdf to distill ps output "ps.usedistiller": validate_ps_distiller, diff --git a/lib/matplotlib/tests/test_backend_pdf.py b/lib/matplotlib/tests/test_backend_pdf.py index 6a6dc1a6bac1..3fcf124e364d 100644 --- a/lib/matplotlib/tests/test_backend_pdf.py +++ b/lib/matplotlib/tests/test_backend_pdf.py @@ -81,48 +81,18 @@ def test_multipage_properfinalize(): def test_multipage_keep_empty(tmp_path): - # test empty pdf files - - # an empty pdf is left behind with keep_empty unset + # An empty pdf deletes itself afterwards. fn = tmp_path / "a.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), PdfPages(fn) as pdf: - pass - assert fn.exists() - - # an empty pdf is left behind with keep_empty=True - fn = tmp_path / "b.pdf" - with (pytest.warns(mpl.MatplotlibDeprecationWarning), - PdfPages(fn, keep_empty=True) as pdf): - pass - assert fn.exists() - - # an empty pdf deletes itself afterwards with keep_empty=False - fn = tmp_path / "c.pdf" - with PdfPages(fn, keep_empty=False) as pdf: + with PdfPages(fn) as pdf: pass assert not fn.exists() - # test pdf files with content, they should never be deleted - - # a non-empty pdf is left behind with keep_empty unset - fn = tmp_path / "d.pdf" + # Test pdf files with content, they should never be deleted. + fn = tmp_path / "b.pdf" with PdfPages(fn) as pdf: pdf.savefig(plt.figure()) assert fn.exists() - # a non-empty pdf is left behind with keep_empty=True - fn = tmp_path / "e.pdf" - with (pytest.warns(mpl.MatplotlibDeprecationWarning), - PdfPages(fn, keep_empty=True) as pdf): - pdf.savefig(plt.figure()) - assert fn.exists() - - # a non-empty pdf is left behind with keep_empty=False - fn = tmp_path / "f.pdf" - with PdfPages(fn, keep_empty=False) as pdf: - pdf.savefig(plt.figure()) - assert fn.exists() - def test_composite_image(): # Test that figures can be saved with and without combining multiple images diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index 54b1c3b5896e..e218a81cdceb 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -288,48 +288,18 @@ def test_pdf_pages_metadata_check(monkeypatch, system): @needs_pgf_xelatex def test_multipage_keep_empty(tmp_path): - # test empty pdf files - - # an empty pdf is left behind with keep_empty unset + # An empty pdf deletes itself afterwards. fn = tmp_path / "a.pdf" - with pytest.warns(mpl.MatplotlibDeprecationWarning), PdfPages(fn) as pdf: - pass - assert fn.exists() - - # an empty pdf is left behind with keep_empty=True - fn = tmp_path / "b.pdf" - with (pytest.warns(mpl.MatplotlibDeprecationWarning), - PdfPages(fn, keep_empty=True) as pdf): - pass - assert fn.exists() - - # an empty pdf deletes itself afterwards with keep_empty=False - fn = tmp_path / "c.pdf" - with PdfPages(fn, keep_empty=False) as pdf: + with PdfPages(fn) as pdf: pass assert not fn.exists() - # test pdf files with content, they should never be deleted - - # a non-empty pdf is left behind with keep_empty unset - fn = tmp_path / "d.pdf" + # Test pdf files with content, they should never be deleted. + fn = tmp_path / "b.pdf" with PdfPages(fn) as pdf: pdf.savefig(plt.figure()) assert fn.exists() - # a non-empty pdf is left behind with keep_empty=True - fn = tmp_path / "e.pdf" - with (pytest.warns(mpl.MatplotlibDeprecationWarning), - PdfPages(fn, keep_empty=True) as pdf): - pdf.savefig(plt.figure()) - assert fn.exists() - - # a non-empty pdf is left behind with keep_empty=False - fn = tmp_path / "f.pdf" - with PdfPages(fn, keep_empty=False) as pdf: - pdf.savefig(plt.figure()) - assert fn.exists() - @needs_pgf_xelatex def test_tex_restart_after_error(): diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index c587a00c0af9..cc968795802e 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -371,10 +371,10 @@ def test_colorbar_shift(tmp_path): plt.colorbar() -def test_auto_papersize_deprecation(): +def test_auto_papersize_removal(): fig = plt.figure() - with pytest.warns(mpl.MatplotlibDeprecationWarning): + with pytest.raises(ValueError, match="'auto' is not a valid value"): fig.savefig(io.BytesIO(), format='eps', papertype='auto') - with pytest.warns(mpl.MatplotlibDeprecationWarning): + with pytest.raises(ValueError, match="'auto' is not a valid value"): mpl.rcParams['ps.papersize'] = 'auto' diff --git a/lib/matplotlib/tests/test_pyplot.py b/lib/matplotlib/tests/test_pyplot.py index 63dc239df2e8..21036e177045 100644 --- a/lib/matplotlib/tests/test_pyplot.py +++ b/lib/matplotlib/tests/test_pyplot.py @@ -439,9 +439,8 @@ def test_switch_backend_no_close(): assert len(plt.get_fignums()) == 2 plt.switch_backend('agg') assert len(plt.get_fignums()) == 2 - with pytest.warns(mpl.MatplotlibDeprecationWarning): - plt.switch_backend('svg') - assert len(plt.get_fignums()) == 0 + plt.switch_backend('svg') + assert len(plt.get_fignums()) == 2 def figure_hook_example(figure): From cf04022f427149f74a612161741de216812756a3 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 24 Sep 2024 22:28:09 -0400 Subject: [PATCH 3/4] Remove 3.8 cbook deprecations --- .../next_api_changes/removals/28874-ES.rst | 13 ++ lib/matplotlib/backend_tools.pyi | 4 +- lib/matplotlib/cbook.py | 120 +----------------- lib/matplotlib/cbook.pyi | 25 +--- 4 files changed, 23 insertions(+), 139 deletions(-) diff --git a/doc/api/next_api_changes/removals/28874-ES.rst b/doc/api/next_api_changes/removals/28874-ES.rst index d5bdaeef6458..0be06b000096 100644 --- a/doc/api/next_api_changes/removals/28874-ES.rst +++ b/doc/api/next_api_changes/removals/28874-ES.rst @@ -61,6 +61,19 @@ which should cover most use cases. ... with no replacement. +``cbook`` API changes +~~~~~~~~~~~~~~~~~~~~~ + +``cbook.Stack`` has been removed with no replacement. + +``Grouper.clean()`` has been removed with no replacement. The Grouper class now cleans +itself up automatically. + +The *np_load* parameter of ``cbook.get_sample_data`` has been removed; `.get_sample_data` +now auto-loads numpy arrays. Use ``get_sample_data(..., asfileobj=False)`` instead to get +the filename of the data file, which can then be passed to `open`, if desired. + + ``axes_grid1`` API changes ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/lib/matplotlib/backend_tools.pyi b/lib/matplotlib/backend_tools.pyi index 446f713292e1..f86a207c7545 100644 --- a/lib/matplotlib/backend_tools.pyi +++ b/lib/matplotlib/backend_tools.pyi @@ -75,8 +75,8 @@ class ToolXScale(AxisScaleBase): def set_scale(self, ax, scale: str | ScaleBase) -> None: ... class ToolViewsPositions(ToolBase): - views: dict[Figure | Axes, cbook.Stack] - positions: dict[Figure | Axes, cbook.Stack] + views: dict[Figure | Axes, cbook._Stack] + positions: dict[Figure | Axes, cbook._Stack] home_views: dict[Figure, dict[Axes, tuple[float, float, float, float]]] def add_figure(self, figure: Figure) -> None: ... def clear(self, figure: Figure) -> None: ... diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 7cf32c4d5f6a..fe556487410f 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -543,9 +543,7 @@ def is_scalar_or_string(val): return isinstance(val, str) or not np.iterable(val) -@_api.delete_parameter( - "3.8", "np_load", alternative="open(get_sample_data(..., asfileobj=False))") -def get_sample_data(fname, asfileobj=True, *, np_load=True): +def get_sample_data(fname, asfileobj=True): """ Return a sample data file. *fname* is a path relative to the :file:`mpl-data/sample_data` directory. If *asfileobj* is `True` @@ -564,10 +562,7 @@ def get_sample_data(fname, asfileobj=True, *, np_load=True): if suffix == '.gz': return gzip.open(path) elif suffix in ['.npy', '.npz']: - if np_load: - return np.load(path) - else: - return path.open('rb') + return np.load(path) elif suffix in ['.csv', '.xrc', '.txt']: return path.open('r') else: @@ -607,113 +602,6 @@ def flatten(seq, scalarp=is_scalar_or_string): yield from flatten(item, scalarp) -@_api.deprecated("3.8") -class Stack: - """ - Stack of elements with a movable cursor. - - Mimics home/back/forward in a web browser. - """ - - def __init__(self, default=None): - self.clear() - self._default = default - - def __call__(self): - """Return the current element, or None.""" - if not self._elements: - return self._default - else: - return self._elements[self._pos] - - def __len__(self): - return len(self._elements) - - def __getitem__(self, ind): - return self._elements[ind] - - def forward(self): - """Move the position forward and return the current element.""" - self._pos = min(self._pos + 1, len(self._elements) - 1) - return self() - - def back(self): - """Move the position back and return the current element.""" - if self._pos > 0: - self._pos -= 1 - return self() - - def push(self, o): - """ - Push *o* to the stack at current position. Discard all later elements. - - *o* is returned. - """ - self._elements = self._elements[:self._pos + 1] + [o] - self._pos = len(self._elements) - 1 - return self() - - def home(self): - """ - Push the first element onto the top of the stack. - - The first element is returned. - """ - if not self._elements: - return - self.push(self._elements[0]) - return self() - - def empty(self): - """Return whether the stack is empty.""" - return len(self._elements) == 0 - - def clear(self): - """Empty the stack.""" - self._pos = -1 - self._elements = [] - - def bubble(self, o): - """ - Raise all references of *o* to the top of the stack, and return it. - - Raises - ------ - ValueError - If *o* is not in the stack. - """ - if o not in self._elements: - raise ValueError('Given element not contained in the stack') - old_elements = self._elements.copy() - self.clear() - top_elements = [] - for elem in old_elements: - if elem == o: - top_elements.append(elem) - else: - self.push(elem) - for _ in top_elements: - self.push(o) - return o - - def remove(self, o): - """ - Remove *o* from the stack. - - Raises - ------ - ValueError - If *o* is not in the stack. - """ - if o not in self._elements: - raise ValueError('Given element not contained in the stack') - old_elements = self._elements.copy() - self.clear() - for elem in old_elements: - if elem != o: - self.push(elem) - - class _Stack: """ Stack of elements with a movable cursor. @@ -913,10 +801,6 @@ def __setstate__(self, state): def __contains__(self, item): return item in self._mapping - @_api.deprecated("3.8", alternative="none, you no longer need to clean a Grouper") - def clean(self): - """Clean dead weak references from the dictionary.""" - def join(self, a, *args): """ Join given arguments into the same set. Accepts one or more arguments. diff --git a/lib/matplotlib/cbook.pyi b/lib/matplotlib/cbook.pyi index d727b8065b7a..cc6b4e8f4e19 100644 --- a/lib/matplotlib/cbook.pyi +++ b/lib/matplotlib/cbook.pyi @@ -74,26 +74,18 @@ def open_file_cm( def is_scalar_or_string(val: Any) -> bool: ... @overload def get_sample_data( - fname: str | os.PathLike, asfileobj: Literal[True] = ..., *, np_load: Literal[True] -) -> np.ndarray: ... + fname: str | os.PathLike, asfileobj: Literal[True] = ... +) -> np.ndarray | IO: ... @overload -def get_sample_data( - fname: str | os.PathLike, - asfileobj: Literal[True] = ..., - *, - np_load: Literal[False] = ..., -) -> IO: ... -@overload -def get_sample_data( - fname: str | os.PathLike, asfileobj: Literal[False], *, np_load: bool = ... -) -> str: ... +def get_sample_data(fname: str | os.PathLike, asfileobj: Literal[False]) -> str: ... def _get_data_path(*args: Path | str) -> Path: ... def flatten( seq: Iterable[Any], scalarp: Callable[[Any], bool] = ... ) -> Generator[Any, None, None]: ... -class Stack(Generic[_T]): - def __init__(self, default: _T | None = ...) -> None: ... +class _Stack(Generic[_T]): + def __init__(self) -> None: ... + def clear(self) -> None: ... def __call__(self) -> _T: ... def __len__(self) -> int: ... def __getitem__(self, ind: int) -> _T: ... @@ -101,10 +93,6 @@ class Stack(Generic[_T]): def back(self) -> _T: ... def push(self, o: _T) -> _T: ... def home(self) -> _T: ... - def empty(self) -> bool: ... - def clear(self) -> None: ... - def bubble(self, o: _T) -> _T: ... - def remove(self, o: _T) -> None: ... def safe_masked_invalid(x: ArrayLike, copy: bool = ...) -> np.ndarray: ... def print_cycles( @@ -114,7 +102,6 @@ def print_cycles( class Grouper(Generic[_T]): def __init__(self, init: Iterable[_T] = ...) -> None: ... def __contains__(self, item: _T) -> bool: ... - def clean(self) -> None: ... def join(self, a: _T, *args: _T) -> None: ... def joined(self, a: _T, b: _T) -> bool: ... def remove(self, a: _T) -> None: ... From d9fdae88f1e9d72fb692632088b3eee3615aaabe Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 25 Sep 2024 01:06:18 -0400 Subject: [PATCH 4/4] Remove remaining 3.8 deprecations --- .../next_api_changes/removals/28874-ES.rst | 71 +++++++++++++++++++ lib/matplotlib/figure.py | 17 ++--- lib/matplotlib/legend.py | 18 +---- lib/matplotlib/path.py | 5 +- lib/matplotlib/table.py | 6 +- lib/matplotlib/tests/test_figure.py | 6 +- lib/matplotlib/tests/test_legend.py | 15 +--- lib/matplotlib/tests/test_table.py | 17 +---- lib/matplotlib/tests/test_widgets.py | 11 --- lib/matplotlib/texmanager.py | 3 +- lib/matplotlib/text.py | 4 -- lib/matplotlib/transforms.py | 13 +--- lib/matplotlib/transforms.pyi | 5 +- lib/matplotlib/widgets.py | 5 -- lib/matplotlib/widgets.pyi | 2 - 15 files changed, 93 insertions(+), 105 deletions(-) diff --git a/doc/api/next_api_changes/removals/28874-ES.rst b/doc/api/next_api_changes/removals/28874-ES.rst index 0be06b000096..dbd8778dead1 100644 --- a/doc/api/next_api_changes/removals/28874-ES.rst +++ b/doc/api/next_api_changes/removals/28874-ES.rst @@ -1,3 +1,48 @@ +Passing extra positional arguments to ``Figure.add_axes`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Positional arguments passed to `.Figure.add_axes` other than a rect or an existing +``Axes`` were previously ignored, and is now an error. + + +Artists explicitly passed in will no longer be filtered by legend() based on their label +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, artists explicitly passed to ``legend(handles=[...])`` are filtered out if +their label starts with an underscore. This filter is no longer applied; explicitly +filter out such artists (``[art for art in artists if not +art.get_label().startswith('_')]``) if necessary. + +Note that if no handles are specified at all, then the default still filters out labels +starting with an underscore. + + +The parameter of ``Annotation.contains`` and ``Legend.contains`` is renamed to *mouseevent* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... consistently with `.Artist.contains`. + + +Support for passing the "frac" key in ``annotate(..., arrowprops={"frac": ...})`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +... has been removed. This key has had no effect since Matplotlib 1.5. + + +Passing non-int or sequence of non-int to ``Table.auto_set_column_width`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Column numbers are ints, and formerly passing any other type was effectively ignored. +This has now become an error. + + +Widgets +~~~~~~~ + +The *visible* attribute getter of ``*Selector`` widgets has been removed; use +``get_visible`` instead. + + Auto-closing of figures when switching backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,6 +106,13 @@ which should cover most use cases. ... with no replacement. +``TexManager.texcache`` +~~~~~~~~~~~~~~~~~~~~~~~ + +... is considered private and has been removed. The location of the cache directory is +clarified in the doc-string. + + ``cbook`` API changes ~~~~~~~~~~~~~~~~~~~~~ @@ -74,6 +126,25 @@ now auto-loads numpy arrays. Use ``get_sample_data(..., asfileobj=False)`` inste the filename of the data file, which can then be passed to `open`, if desired. +Calling ``paths.get_path_collection_extents`` with empty *offsets* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Calling `~.get_path_collection_extents` with an empty *offsets* parameter has an +ambiguous interpretation and is no longer allowed. + + +``bbox.anchored()`` with no explicit container +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Not passing a *container* argument to `.BboxBase.anchored` is no longer supported. + + +``INVALID_NON_AFFINE``, ``INVALID_AFFINE``, ``INVALID`` attributes of ``TransformNode`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +These attributes have been removed. + + ``axes_grid1`` API changes ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 73f1629180aa..4271bb78e8de 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -613,22 +613,22 @@ def add_axes(self, *args, **kwargs): """ if not len(args) and 'rect' not in kwargs: - raise TypeError( - "add_axes() missing 1 required positional argument: 'rect'") + raise TypeError("add_axes() missing 1 required positional argument: 'rect'") elif 'rect' in kwargs: if len(args): - raise TypeError( - "add_axes() got multiple values for argument 'rect'") + raise TypeError("add_axes() got multiple values for argument 'rect'") args = (kwargs.pop('rect'), ) + if len(args) != 1: + raise _api.nargs_error("add_axes", 1, len(args)) if isinstance(args[0], Axes): - a, *extra_args = args + a, = args key = a._projection_init if a.get_figure(root=False) is not self: raise ValueError( "The Axes must have been created in the present figure") else: - rect, *extra_args = args + rect, = args if not np.isfinite(rect).all(): raise ValueError(f'all entries in rect must be finite not {rect}') projection_class, pkw = self._process_projection_requirements(**kwargs) @@ -637,11 +637,6 @@ def add_axes(self, *args, **kwargs): a = projection_class(self, rect, **pkw) key = (projection_class, pkw) - if extra_args: - _api.warn_deprecated( - "3.8", - name="Passing more than one positional argument to Figure.add_axes", - addendum="Any additional positional arguments are currently ignored.") return self._add_axes_internal(a, key) @_docstring.interpd diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 270757fc298e..a3bf4c14e7fc 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -454,24 +454,10 @@ def __init__( self.borderaxespad = mpl._val_or_rc(borderaxespad, 'legend.borderaxespad') self.columnspacing = mpl._val_or_rc(columnspacing, 'legend.columnspacing') self.shadow = mpl._val_or_rc(shadow, 'legend.shadow') - # trim handles and labels if illegal label... - _lab, _hand = [], [] - for label, handle in zip(labels, handles): - if isinstance(label, str) and label.startswith('_'): - _api.warn_deprecated("3.8", message=( - "An artist whose label starts with an underscore was passed to " - "legend(); such artists will no longer be ignored in the future. " - "To suppress this warning, explicitly filter out such artists, " - "e.g. with `[art for art in artists if not " - "art.get_label().startswith('_')]`.")) - else: - _lab.append(label) - _hand.append(handle) - labels, handles = _lab, _hand if reverse: - labels.reverse() - handles.reverse() + labels = [*reversed(labels)] + handles = [*reversed(handles)] if len(handles) < 2: ncols = 1 diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 0e870161a48d..5f5a0f3de423 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -1086,10 +1086,7 @@ def get_path_collection_extents( if len(paths) == 0: raise ValueError("No paths provided") if len(offsets) == 0: - _api.warn_deprecated( - "3.8", message="Calling get_path_collection_extents() with an" - " empty offsets list is deprecated since %(since)s. Support will" - " be removed %(removal)s.") + raise ValueError("No offsets provided") extents, minpos = _path.get_path_collection_extents( master_transform, paths, np.atleast_3d(transforms), offsets, offset_transform) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 0f75021926fd..212cd9f45187 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -496,11 +496,7 @@ def auto_set_column_width(self, col): """ col1d = np.atleast_1d(col) if not np.issubdtype(col1d.dtype, np.integer): - _api.warn_deprecated("3.8", name="col", - message="%(name)r must be an int or sequence of ints. " - "Passing other types is deprecated since %(since)s " - "and will be removed %(removal)s.") - return + raise TypeError("col must be an int or sequence of ints.") for cell in col1d: self._autoColumns.append(cell) diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 99045e773d02..528df182a2d0 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -518,12 +518,10 @@ def test_invalid_figure_add_axes(): fig.add_axes(ax) fig2.delaxes(ax) - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="Passing more than one positional argument"): + with pytest.raises(TypeError, match=r"add_axes\(\) takes 1 positional arguments"): fig2.add_axes(ax, "extra positional argument") - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="Passing more than one positional argument"): + with pytest.raises(TypeError, match=r"add_axes\(\) takes 1 positional arguments"): fig.add_axes([0, 0, 1, 1], "extra positional argument") diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 62b40ddb2d7a..cf0a950de1ca 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -19,7 +19,7 @@ import matplotlib.lines as mlines from matplotlib.legend_handler import HandlerTuple import matplotlib.legend as mlegend -from matplotlib import _api, rc_context +from matplotlib import rc_context from matplotlib.font_manager import FontProperties @@ -138,19 +138,6 @@ def test_various_labels(): ax.legend(numpoints=1, loc='best') -def test_legend_label_with_leading_underscore(): - """ - Test that artists with labels starting with an underscore are not added to - the legend, and that a warning is issued if one tries to add them - explicitly. - """ - fig, ax = plt.subplots() - line, = ax.plot([0, 1], label='_foo') - with pytest.warns(_api.MatplotlibDeprecationWarning, match="with an underscore"): - legend = ax.legend(handles=[line]) - assert len(legend.legend_handles) == 0 - - @image_comparison(['legend_labels_first.png'], remove_text=True, tol=0.013 if platform.machine() == 'arm64' else 0) def test_labels_first(): diff --git a/lib/matplotlib/tests/test_table.py b/lib/matplotlib/tests/test_table.py index ea31ac124e4a..653e918eecc8 100644 --- a/lib/matplotlib/tests/test_table.py +++ b/lib/matplotlib/tests/test_table.py @@ -2,10 +2,8 @@ from unittest.mock import Mock import numpy as np -import pytest import matplotlib.pyplot as plt -import matplotlib as mpl from matplotlib.path import Path from matplotlib.table import CustomCell, Table from matplotlib.testing.decorators import image_comparison, check_figures_equal @@ -128,10 +126,9 @@ def test_customcell(): @image_comparison(['table_auto_column.png']) def test_auto_column(): - fig = plt.figure() + fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1) # iterable list input - ax1 = fig.add_subplot(4, 1, 1) ax1.axis('off') tb1 = ax1.table( cellText=[['Fit Text', 2], @@ -144,7 +141,6 @@ def test_auto_column(): tb1.auto_set_column_width([-1, 0, 1]) # iterable tuple input - ax2 = fig.add_subplot(4, 1, 2) ax2.axis('off') tb2 = ax2.table( cellText=[['Fit Text', 2], @@ -157,7 +153,6 @@ def test_auto_column(): tb2.auto_set_column_width((-1, 0, 1)) # 3 single inputs - ax3 = fig.add_subplot(4, 1, 3) ax3.axis('off') tb3 = ax3.table( cellText=[['Fit Text', 2], @@ -171,8 +166,8 @@ def test_auto_column(): tb3.auto_set_column_width(0) tb3.auto_set_column_width(1) - # 4 non integer iterable input - ax4 = fig.add_subplot(4, 1, 4) + # 4 this used to test non-integer iterable input, which did nothing, but only + # remains to avoid re-generating the test image. ax4.axis('off') tb4 = ax4.table( cellText=[['Fit Text', 2], @@ -182,12 +177,6 @@ def test_auto_column(): loc="center") tb4.auto_set_font_size(False) tb4.set_fontsize(12) - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="'col' must be an int or sequence of ints"): - tb4.auto_set_column_width("-101") # type: ignore [arg-type] - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="'col' must be an int or sequence of ints"): - tb4.auto_set_column_width(["-101"]) # type: ignore [list-item] def test_table_cells(): diff --git a/lib/matplotlib/tests/test_widgets.py b/lib/matplotlib/tests/test_widgets.py index 58238cd08af2..585d846944e8 100644 --- a/lib/matplotlib/tests/test_widgets.py +++ b/lib/matplotlib/tests/test_widgets.py @@ -3,7 +3,6 @@ import operator from unittest import mock -import matplotlib as mpl from matplotlib.backend_bases import MouseEvent import matplotlib.colors as mcolors import matplotlib.widgets as widgets @@ -131,16 +130,6 @@ def test_rectangle_minspan(ax, spancoords, minspanx, x1, minspany, y1): assert kwargs == {} -def test_deprecation_selector_visible_attribute(ax): - tool = widgets.RectangleSelector(ax) - - assert tool.get_visible() - - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match="was deprecated in Matplotlib 3.8"): - tool.visible - - @pytest.mark.parametrize('drag_from_anywhere, new_center', [[True, (60, 75)], [False, (30, 20)]]) diff --git a/lib/matplotlib/texmanager.py b/lib/matplotlib/texmanager.py index 8deb03b3e148..a374bfba8cab 100644 --- a/lib/matplotlib/texmanager.py +++ b/lib/matplotlib/texmanager.py @@ -31,7 +31,7 @@ import numpy as np import matplotlib as mpl -from matplotlib import _api, cbook, dviread +from matplotlib import cbook, dviread _log = logging.getLogger(__name__) @@ -63,7 +63,6 @@ class TexManager: Repeated calls to this constructor always return the same instance. """ - texcache = _api.deprecate_privatize_attribute("3.8") _texcache = os.path.join(mpl.get_cachedir(), 'tex.cache') _grey_arrayd = {} diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 6691f32f8771..782dc9754e52 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -1842,10 +1842,6 @@ def transform(renderer) -> Transform # modified YAArrow API to be used with FancyArrowPatch for key in ['width', 'headwidth', 'headlength', 'shrink']: arrowprops.pop(key, None) - if 'frac' in arrowprops: - _api.warn_deprecated( - "3.8", name="the (unused) 'frac' key in 'arrowprops'") - arrowprops.pop("frac") self.arrow_patch = FancyArrowPatch((0, 0), (1, 1), **arrowprops) else: self.arrow_patch = None diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index fde6d6732171..15caff545e73 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -93,9 +93,6 @@ class TransformNode: # Invalidation may affect only the affine part. If the # invalidation was "affine-only", the _invalid member is set to # INVALID_AFFINE_ONLY - INVALID_NON_AFFINE = _api.deprecated("3.8")(_api.classproperty(lambda cls: 1)) - INVALID_AFFINE = _api.deprecated("3.8")(_api.classproperty(lambda cls: 2)) - INVALID = _api.deprecated("3.8")(_api.classproperty(lambda cls: 3)) # Possible values for the _invalid attribute. _VALID, _INVALID_AFFINE_ONLY, _INVALID_FULL = range(3) @@ -480,7 +477,7 @@ def transformed(self, transform): 'NW': (0, 1.0), 'W': (0, 0.5)} - def anchored(self, c, container=None): + def anchored(self, c, container): """ Return a copy of the `Bbox` anchored to *c* within *container*. @@ -490,19 +487,13 @@ def anchored(self, c, container=None): Either an (*x*, *y*) pair of relative coordinates (0 is left or bottom, 1 is right or top), 'C' (center), or a cardinal direction ('SW', southwest, is bottom left, etc.). - container : `Bbox`, optional + container : `Bbox` The box within which the `Bbox` is positioned. See Also -------- .Axes.set_anchor """ - if container is None: - _api.warn_deprecated( - "3.8", message="Calling anchored() with no container bbox " - "returns a frozen copy of the original bbox and is deprecated " - "since %(since)s.") - container = self l, b, w, h = container.bounds L, B, W, H = self.bounds cx, cy = self.coefs[c] if isinstance(c, str) else c diff --git a/lib/matplotlib/transforms.pyi b/lib/matplotlib/transforms.pyi index 90a527e5bfc5..c87a965b1e4a 100644 --- a/lib/matplotlib/transforms.pyi +++ b/lib/matplotlib/transforms.pyi @@ -77,9 +77,10 @@ class BboxBase(TransformNode): def fully_overlaps(self, other: BboxBase) -> bool: ... def transformed(self, transform: Transform) -> Bbox: ... coefs: dict[str, tuple[float, float]] - # anchored type can be s/str/Literal["C", "SW", "S", "SE", "E", "NE", "N", "NW", "W"] def anchored( - self, c: tuple[float, float] | str, container: BboxBase | None = ... + self, + c: tuple[float, float] | Literal['C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'], + container: BboxBase, ) -> Bbox: ... def shrunk(self, mx: float, my: float) -> Bbox: ... def shrunk_to_aspect( diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 0e8eaa68b6b4..9c676574310c 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -2350,11 +2350,6 @@ def get_visible(self): """Get the visibility of the selector artists.""" return self._visible - @property - def visible(self): - _api.warn_deprecated("3.8", alternative="get_visible") - return self.get_visible() - def clear(self): """Clear the selection and set the selector ready to make a new one.""" self._clear_without_update() diff --git a/lib/matplotlib/widgets.pyi b/lib/matplotlib/widgets.pyi index 96bc0c431ac3..0fcd1990e17e 100644 --- a/lib/matplotlib/widgets.pyi +++ b/lib/matplotlib/widgets.pyi @@ -294,8 +294,6 @@ class _SelectorWidget(AxesWidget): def on_key_release(self, event: Event) -> None: ... def set_visible(self, visible: bool) -> None: ... def get_visible(self) -> bool: ... - @property - def visible(self) -> bool: ... def clear(self) -> None: ... @property def artists(self) -> tuple[Artist]: ...