From 60d4906a9bbaebad99006cc052455409bfb14572 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 16 Jun 2021 22:43:41 -0400 Subject: [PATCH 1/8] Remove case-insensitivity deprecated in 3.3. --- .../next_api_changes/removals/20465-ES.rst | 10 +++++++ lib/matplotlib/_enums.py | 27 +------------------ lib/matplotlib/artist.py | 8 +----- 3 files changed, 12 insertions(+), 33 deletions(-) create mode 100644 doc/api/next_api_changes/removals/20465-ES.rst diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst new file mode 100644 index 000000000000..87ecb84b5a76 --- /dev/null +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -0,0 +1,10 @@ +Case-insensitive properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Upper or mixed-case property names are no longer normalized to lowercase in +`.Artist.set` and `.Artist.update`. This allows one to pass names such as +*patchA* or *UVC*. + +Case-insensitive capstyles and joinstyles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Please pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", +"round", "projecting") as lowercase. diff --git a/lib/matplotlib/_enums.py b/lib/matplotlib/_enums.py index 35fe82482869..3e4393e7979d 100644 --- a/lib/matplotlib/_enums.py +++ b/lib/matplotlib/_enums.py @@ -11,7 +11,7 @@ """ from enum import Enum, auto -from matplotlib import cbook, docstring +from matplotlib import docstring class _AutoStringNameEnum(Enum): @@ -24,23 +24,6 @@ def __hash__(self): return str(self).__hash__() -def _deprecate_case_insensitive_join_cap(s): - s_low = s.lower() - if s != s_low: - if s_low in ['miter', 'round', 'bevel']: - cbook.warn_deprecated( - "3.3", message="Case-insensitive capstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - elif s_low in ['butt', 'round', 'projecting']: - cbook.warn_deprecated( - "3.3", message="Case-insensitive joinstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - # Else, error out at the check_in_list stage. - return s_low - - class JoinStyle(str, _AutoStringNameEnum): """ Define how the connection between two line segments is drawn. @@ -100,10 +83,6 @@ class JoinStyle(str, _AutoStringNameEnum): round = auto() bevel = auto() - def __init__(self, s): - s = _deprecate_case_insensitive_join_cap(s) - Enum.__init__(self) - @staticmethod def demo(): """Demonstrate how each JoinStyle looks for various join angles.""" @@ -173,10 +152,6 @@ class CapStyle(str, _AutoStringNameEnum): projecting = 'projecting' round = 'round' - def __init__(self, s): - s = _deprecate_case_insensitive_join_cap(s) - Enum.__init__(self) - @staticmethod def demo(): """Demonstrate how each CapStyle looks for a thick line segment.""" diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 21eb9820f529..1d2c73e6f627 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -1041,13 +1041,7 @@ def update(self, props): ret = [] with cbook._setattr_cm(self, eventson=False): for k, v in props.items(): - if k != k.lower(): - _api.warn_deprecated( - "3.3", message="Case-insensitive properties were " - "deprecated in %(since)s and support will be removed " - "%(removal)s") - k = k.lower() - # White list attributes we want to be able to update through + # Allow attributes we want to be able to update through # art.update, art.set, setp. if k == "axes": ret.append(setattr(self, k, v)) From b2667e8d02d8673b8aa2caf7a3ae169cee44152c Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 00:21:42 -0400 Subject: [PATCH 2/8] Remove animation bits deprecated in 3.3. --- doc/api/animation_api.rst | 5 +- .../next_api_changes/removals/20465-ES.rst | 20 ++++ lib/matplotlib/animation.py | 95 ++----------------- lib/matplotlib/rcsetup.py | 10 +- 4 files changed, 28 insertions(+), 102 deletions(-) diff --git a/doc/api/animation_api.rst b/doc/api/animation_api.rst index 2e052b53a5ce..4ccded4d8ca4 100644 --- a/doc/api/animation_api.rst +++ b/doc/api/animation_api.rst @@ -186,7 +186,6 @@ on all systems. FFMpegWriter ImageMagickWriter - AVConvWriter The file-based writers save temporary files for each frame which are stitched into a single file at the end. Although slower, these writers can be easier to @@ -198,7 +197,6 @@ debug. FFMpegFileWriter ImageMagickFileWriter - AVConvFileWriter Fundamentally, a `MovieWriter` provides a way to grab sequential frames from the same underlying `~matplotlib.figure.Figure` object. The base @@ -283,7 +281,6 @@ and mixins :toctree: _as_gen :nosignatures: - AVConvBase FFMpegBase ImageMagickBase @@ -298,6 +295,6 @@ Inheritance Diagrams :private-bases: :parts: 1 -.. inheritance-diagram:: matplotlib.animation.AVConvFileWriter matplotlib.animation.AVConvWriter matplotlib.animation.FFMpegFileWriter matplotlib.animation.FFMpegWriter matplotlib.animation.ImageMagickFileWriter matplotlib.animation.ImageMagickWriter +.. inheritance-diagram:: matplotlib.animation.FFMpegFileWriter matplotlib.animation.FFMpegWriter matplotlib.animation.ImageMagickFileWriter matplotlib.animation.ImageMagickWriter :private-bases: :parts: 1 diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index 87ecb84b5a76..1d89f5d37ecc 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -8,3 +8,23 @@ Case-insensitive capstyles and joinstyles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Please pass capstyles ("miter", "round", "bevel") and joinstyles ("butt", "round", "projecting") as lowercase. + +AVConv animation writers removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``AVConvBase``, ``AVConvWriter`` and ``AVConvFileWriter`` classes, and the +associated ``animation.avconv_path`` and ``animation.avconv_args`` rcParams +have been removed. + +Debian 8 (2015, EOL 06/2020) and Ubuntu 14.04 (EOL 04/2019) were the +last versions of Debian and Ubuntu to ship avconv. It remains possible +to force the use of avconv by using the FFmpeg-based writers with +:rc:`animation.ffmpeg_path` set to "avconv". + +``MovieWriter`` attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~ +* ``animation.html_args`` rcParam +* ``HTMLWriter.args_key`` attribute +* ``MovieWriter.args_key`` and ``MovieWriter.exec_key`` attributes +* *clear_temp* parameter and attribute of `.FileMovieWriter`; files placed in a + temporary directory (using ``frame_prefix=None``, the default) will be + cleared; files placed elsewhere will not. diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py index c15fa086eb15..4ad431302e5c 100644 --- a/lib/matplotlib/animation.py +++ b/lib/matplotlib/animation.py @@ -261,9 +261,6 @@ class MovieWriter(AbstractMovieWriter): # stored. Third-party writers cannot meaningfully set these as they cannot # extend rcParams with new keys. - exec_key = _api.deprecate_privatize_attribute("3.3") - args_key = _api.deprecate_privatize_attribute("3.3") - # Pipe-based writers only support RGBA, but file-based ones support more # formats. supported_formats = ["rgba"] @@ -407,9 +404,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.frame_format = mpl.rcParams['animation.frame_format'] - @_api.delete_parameter("3.3", "clear_temp") - def setup(self, fig, outfile, dpi=None, frame_prefix=None, - clear_temp=True): + def setup(self, fig, outfile, dpi=None, frame_prefix=None): """ Setup for writing the movie file. @@ -423,13 +418,10 @@ def setup(self, fig, outfile, dpi=None, frame_prefix=None, The dpi of the output file. This, with the figure size, controls the size in pixels of the resulting movie file. frame_prefix : str, optional - The filename prefix to use for temporary files. If None (the + The filename prefix to use for temporary files. If *None* (the default), files are written to a temporary directory which is - deleted by `cleanup` (regardless of the value of *clear_temp*). - clear_temp : bool, optional - If the temporary files should be deleted after stitching - the final result. Setting this to ``False`` can be useful for - debugging. Defaults to ``True``. + deleted by `cleanup`; if not *None*, no temporary files are + deleted. """ self.fig = fig self.outfile = outfile @@ -444,7 +436,6 @@ def setup(self, fig, outfile, dpi=None, frame_prefix=None, else: self._tmpdir = None self.temp_prefix = frame_prefix - self._clear_temp = clear_temp self._frame_counter = 0 # used for generating sequential file names self._temp_paths = list() self.fname_format_str = '%s%%07d.%s' @@ -453,15 +444,6 @@ def __del__(self): if self._tmpdir: self._tmpdir.cleanup() - @_api.deprecated("3.3") - @property - def clear_temp(self): - return self._clear_temp - - @clear_temp.setter - def clear_temp(self, value): - self._clear_temp = value - @property def frame_format(self): """ @@ -488,10 +470,9 @@ def _base_temp_name(self): def grab_frame(self, **savefig_kwargs): # docstring inherited - # Overloaded to explicitly close temp file. # Creates a filename for saving using basename and counter. path = Path(self._base_temp_name() % self._frame_counter) - self._temp_paths.append(path) # Record the filename for later cleanup. + self._temp_paths.append(path) # Record the filename for later use. self._frame_counter += 1 # Ensures each created name is unique. _log.debug('FileMovieWriter.grab_frame: Grabbing frame %d to path=%s', self._frame_counter, path) @@ -510,12 +491,6 @@ def _cleanup(self): # Inline to finish() once cleanup() is removed. if self._tmpdir: _log.debug('MovieWriter: clearing temporary path=%s', self._tmpdir) self._tmpdir.cleanup() - else: - if self._clear_temp: - _log.debug('MovieWriter: clearing temporary paths=%s', - self._temp_paths) - for path in self._temp_paths: - path.unlink() @writers.register('pillow') @@ -582,17 +557,6 @@ def output_args(self): return args + ['-y', self.outfile] - @classmethod - def isAvailable(cls): - return ( - super().isAvailable() - # Ubuntu 12.04 ships a broken ffmpeg binary which we shouldn't use. - # NOTE: when removed, remove the same method in AVConvBase. - and b'LibAv' not in subprocess.run( - [cls.bin_path()], creationflags=subprocess_creation_flags, - stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE).stderr) - # Combine FFMpeg options with pipe-based writing @writers.register('ffmpeg') @@ -651,45 +615,6 @@ def _args(self): return [self.bin_path(), *args, *self.output_args] -# Base class of avconv information. AVConv has identical arguments to FFMpeg. -@_api.deprecated('3.3') -class AVConvBase(FFMpegBase): - """ - Mixin class for avconv output. - - To be useful this must be multiply-inherited from with a - `MovieWriterBase` sub-class. - """ - - _exec_key = 'animation.avconv_path' - _args_key = 'animation.avconv_args' - - # NOTE : should be removed when the same method is removed in FFMpegBase. - isAvailable = classmethod(MovieWriter.isAvailable.__func__) - - -# Combine AVConv options with pipe-based writing -@writers.register('avconv') -class AVConvWriter(AVConvBase, FFMpegWriter): - """ - Pipe-based avconv writer. - - Frames are streamed directly to avconv via a pipe and written in a single - pass. - """ - - -# Combine AVConv options with file-based writing -@writers.register('avconv_file') -class AVConvFileWriter(AVConvBase, FFMpegFileWriter): - """ - File-based avconv writer. - - Frames are written to temporary files on disk and then stitched - together at the end. - """ - - # Base class for animated GIFs with ImageMagick class ImageMagickBase: """ @@ -794,8 +719,6 @@ class HTMLWriter(FileMovieWriter): """Writer for JavaScript-based HTML movies.""" supported_formats = ['png', 'jpeg', 'tiff', 'svg'] - args_key = _api.deprecated("3.3")(property( - lambda self: 'animation.html_args')) @classmethod def isAvailable(cls): @@ -892,19 +815,13 @@ def finish(self): # duplicate the temporary file clean up logic from # FileMovieWriter.cleanup. We can not call the inherited - # versions of finished or cleanup because both assume that + # versions of finish or cleanup because both assume that # there is a subprocess that we either need to call to merge # many frames together or that there is a subprocess call that # we need to clean up. if self._tmpdir: _log.debug('MovieWriter: clearing temporary path=%s', self._tmpdir) self._tmpdir.cleanup() - else: - if self._clear_temp: - _log.debug('MovieWriter: clearing temporary paths=%s', - self._temp_paths) - for path in self._temp_paths: - path.unlink() class Animation: diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index fdf54f288850..13068d5d375f 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1236,16 +1236,10 @@ def _convert_validator_spec(key, conv): # Controls image format when frames are written to disk "animation.frame_format": ["png", "jpeg", "tiff", "raw", "rgba", "ppm", "sgi", "bmp", "pbm", "svg"], - # Additional arguments for HTML writer - "animation.html_args": validate_stringlist, # Path to ffmpeg binary. If just binary name, subprocess uses $PATH. "animation.ffmpeg_path": _validate_pathlike, # Additional arguments for ffmpeg movie writer (using pipes) "animation.ffmpeg_args": validate_stringlist, - # Path to AVConv binary. If just binary name, subprocess uses $PATH. - "animation.avconv_path": _validate_pathlike, - # Additional arguments for avconv movie writer (using pipes) - "animation.avconv_args": validate_stringlist, # Path to convert binary. If just binary name, subprocess uses $PATH. "animation.convert_path": _validate_pathlike, # Additional arguments for convert movie writer (using pipes) @@ -1261,9 +1255,7 @@ def _convert_validator_spec(key, conv): # ... because they are private: "_internal.classic_mode": False, # ... because they are deprecated: - "animation.avconv_path": "avconv", - "animation.avconv_args": [], - "animation.html_args": [], + # No current deprecations. # backend is handled separately when constructing rcParamsDefault. } _validators = {k: _convert_validator_spec(k, conv) From c51ae6d54d0e8243f601548be415aca645fa08b5 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 01:10:13 -0400 Subject: [PATCH 3/8] Remove artist-specific deprecations from 3.3. --- doc/api/artist_api.rst | 2 - .../next_api_changes/removals/20465-ES.rst | 17 +++++++ lib/matplotlib/artist.py | 47 +------------------ lib/matplotlib/image.py | 20 ++------ lib/matplotlib/lines.py | 8 +--- lib/matplotlib/patches.py | 29 +++--------- lib/matplotlib/streamplot.py | 8 +--- 7 files changed, 31 insertions(+), 100 deletions(-) diff --git a/doc/api/artist_api.rst b/doc/api/artist_api.rst index d77f27f0960f..98bfb1f057c8 100644 --- a/doc/api/artist_api.rst +++ b/doc/api/artist_api.rst @@ -36,8 +36,6 @@ Interactive Artist.format_cursor_data Artist.mouseover Artist.contains - Artist.set_contains - Artist.get_contains Artist.pick Artist.pickable Artist.set_picker diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index 1d89f5d37ecc..4d81afe7d5f1 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -28,3 +28,20 @@ to force the use of avconv by using the FFmpeg-based writers with * *clear_temp* parameter and attribute of `.FileMovieWriter`; files placed in a temporary directory (using ``frame_prefix=None``, the default) will be cleared; files placed elsewhere will not. + +Artist-specific removals +~~~~~~~~~~~~~~~~~~~~~~~~ +* Setting a custom method overriding `.Artist.contains` using + ``Artist.set_contains`` has been removed, as has ``Artist.get_contains``. + There is no replacement, but you may still customize pick events using + `.Artist.set_picker`. +* Passing the dash offset as ``None`` is no longer accepted, as this was never + universally implemented, e.g. for vector output. Set the offset to 0 instead. +* The parameter *props* of `.Shadow` has been removed. Use keyword arguments + instead. +* Arbitrary keyword arguments to ``StreamplotSet`` have no effect and have been + removed. +* ``NonUniformImage.is_grayscale`` and ``PcolorImage.is_grayscale`` attributes + have been removed, for consistency with ``AxesImage.is_grayscale``. (Note + that previously, these attributes were only available *after rendering the + image*). diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index 1d2c73e6f627..1fa0a8e5b23b 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -109,7 +109,6 @@ def __init__(self): self._clipon = True self._label = '' self._picker = None - self._contains = None self._rasterized = False self._agg_filter = None # Normally, artist classes need to be queried for mouseover info if and @@ -401,10 +400,9 @@ def _default_contains(self, mouseevent, figure=None): """ Base impl. for checking whether a mouseevent happened in an artist. - 1. If the artist defines a custom checker, use it (deprecated). - 2. If the artist figure is known and the event did not occur in that + 1. If the artist figure is known and the event did not occur in that figure (by checking its ``canvas`` attribute), reject it. - 3. Otherwise, return `None, {}`, indicating that the subclass' + 2. Otherwise, return `None, {}`, indicating that the subclass' implementation should be used. Subclasses should start their definition of `contains` as follows: @@ -417,8 +415,6 @@ def _default_contains(self, mouseevent, figure=None): The *figure* kwarg is provided for the implementation of `.Figure.contains`. """ - if callable(self._contains): - return self._contains(self, mouseevent) if figure is not None and mouseevent.canvas is not figure.canvas: return False, {} return None, {} @@ -446,45 +442,6 @@ def contains(self, mouseevent): _log.warning("%r needs 'contains' method", self.__class__.__name__) return False, {} - @_api.deprecated("3.3", alternative="set_picker") - def set_contains(self, picker): - """ - Define a custom contains test for the artist. - - The provided callable replaces the default `.contains` method - of the artist. - - Parameters - ---------- - picker : callable - A custom picker function to evaluate if an event is within the - artist. The function must have the signature:: - - def contains(artist: Artist, event: MouseEvent) -> bool, dict - - that returns: - - - a bool indicating if the event is within the artist - - a dict of additional information. The dict should at least - return the same information as the default ``contains()`` - implementation of the respective artist, but may provide - additional information. - """ - if not callable(picker): - raise TypeError("picker is not a callable") - self._contains = picker - - @_api.deprecated("3.3", alternative="get_picker") - def get_contains(self): - """ - Return the custom contains function of the artist if set, or *None*. - - See Also - -------- - set_contains - """ - return self._contains - def pickable(self): """ Return whether the artist is pickable. diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index b710e7ac0901..a59499cd966b 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -1015,8 +1015,6 @@ def _check_unsampled_image(self): """Return False. Do not use unsampled image.""" return False - is_grayscale = _api.deprecate_privatize_attribute("3.3") - def make_image(self, renderer, magnification=1.0, unsampled=False): # docstring inherited if self._A is None: @@ -1027,11 +1025,9 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): if A.ndim == 2: if A.dtype != np.uint8: A = self.to_rgba(A, bytes=True) - self._is_grayscale = self.cmap.is_gray() else: A = np.repeat(A[:, :, np.newaxis], 4, 2) A[:, :, 3] = 255 - self._is_grayscale = True else: if A.dtype != np.uint8: A = (255*A).astype(np.uint8) @@ -1040,7 +1036,6 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): B[:, :, 0:3] = A B[:, :, 3] = 255 A = B - self._is_grayscale = False vl = self.axes.viewLim l, b, r, t = self.axes.bbox.extents width = int(((round(r) + 0.5) - (round(l) - 0.5)) * magnification) @@ -1203,8 +1198,6 @@ def __init__(self, ax, if A is not None: self.set_data(x, y, A) - is_grayscale = _api.deprecate_privatize_attribute("3.3") - def make_image(self, renderer, magnification=1.0, unsampled=False): # docstring inherited if self._A is None: @@ -1215,8 +1208,6 @@ def make_image(self, renderer, magnification=1.0, unsampled=False): if self._rgbacache is None: A = self.to_rgba(self._A, bytes=True) self._rgbacache = np.pad(A, [(1, 1), (1, 1), (0, 0)], "constant") - if self._A.ndim == 2: - self._is_grayscale = self.cmap.is_gray() padded_A = self._rgbacache bg = mcolors.to_rgba(self.axes.patch.get_facecolor(), 0) bg = (np.array(bg) * 255).astype(np.uint8) @@ -1277,15 +1268,10 @@ def set_data(self, x, y, A): (A.shape[:2], (y.size - 1, x.size - 1))) if A.ndim not in [2, 3]: raise ValueError("A must be 2D or 3D") - if A.ndim == 3 and A.shape[2] == 1: - A = A.squeeze(axis=-1) - self._is_grayscale = False if A.ndim == 3: - if A.shape[2] in [3, 4]: - if ((A[:, :, 0] == A[:, :, 1]).all() and - (A[:, :, 0] == A[:, :, 2]).all()): - self._is_grayscale = True - else: + if A.shape[2] == 1: + A = A.squeeze(axis=-1) + elif A.shape[2] not in [3, 4]: raise ValueError("3D arrays must have RGB or RGBA as last dim") # For efficient cursor readout, ensure x and y are increasing. diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index c96fc27c173d..2ddfbdbfe296 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -45,13 +45,9 @@ def _get_dash_pattern(style): elif isinstance(style, tuple): offset, dashes = style if offset is None: - _api.warn_deprecated( - "3.3", message="Passing the dash offset as None is deprecated " - "since %(since)s and support for it will be removed " - "%(removal)s; pass it as zero instead.") - offset = 0 + raise ValueError(f'Unrecognized linestyle: {style!r}') else: - raise ValueError('Unrecognized linestyle: %s' % str(style)) + raise ValueError(f'Unrecognized linestyle: {style!r}') # normalize offset to be positive and shorter than the dash cycle if dashes is not None: diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 00df0943a57f..91f7bbee37f2 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -636,9 +636,8 @@ class Shadow(Patch): def __str__(self): return "Shadow(%s)" % (str(self.patch)) - @_api.delete_parameter("3.3", "props") @docstring.dedent_interpd - def __init__(self, patch, ox, oy, props=None, **kwargs): + def __init__(self, patch, ox, oy, **kwargs): """ Create a shadow of the given *patch*. @@ -652,8 +651,6 @@ def __init__(self, patch, ox, oy, props=None, **kwargs): ox, oy : float The shift of the shadow in data coordinates, scaled by a factor of dpi/72. - props : dict - *deprecated (use kwargs instead)* Properties of the shadow patch. **kwargs Properties of the shadow patch. Supported keys are: @@ -661,29 +658,15 @@ def __init__(self, patch, ox, oy, props=None, **kwargs): """ super().__init__() self.patch = patch - # Note: when removing props, we can directly pass kwargs to _update() - # and remove self._props - if props is None: - color = .3 * np.asarray(colors.to_rgb(self.patch.get_facecolor())) - props = { - 'facecolor': color, - 'edgecolor': color, - 'alpha': 0.5, - } - self._props = {**props, **kwargs} self._ox, self._oy = ox, oy self._shadow_transform = transforms.Affine2D() - self._update() - props = _api.deprecate_privatize_attribute("3.3") - - def _update(self): self.update_from(self.patch) - - # Place the shadow patch directly behind the inherited patch. - self.set_zorder(np.nextafter(self.patch.zorder, -np.inf)) - - self.update(self._props) + color = .3 * np.asarray(colors.to_rgb(self.patch.get_facecolor())) + self.update({'facecolor': color, 'edgecolor': color, 'alpha': 0.5, + # Place shadow patch directly behind the inherited patch. + 'zorder': np.nextafter(self.patch.zorder, -np.inf), + **kwargs}) def _update_transform(self, renderer): ox = renderer.points_to_pixels(self._ox) diff --git a/lib/matplotlib/streamplot.py b/lib/matplotlib/streamplot.py index d7cd9f38c924..24c12fa3b5da 100644 --- a/lib/matplotlib/streamplot.py +++ b/lib/matplotlib/streamplot.py @@ -236,13 +236,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, class StreamplotSet: - def __init__(self, lines, arrows, **kwargs): - if kwargs: - _api.warn_deprecated( - "3.3", - message="Passing arbitrary keyword arguments to StreamplotSet " - "is deprecated since %(since) and will become an " - "error %(removal)s.") + def __init__(self, lines, arrows): self.lines = lines self.arrows = arrows From 372741c34a01a7d5321eecde20202f52f22bde52 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 01:25:31 -0400 Subject: [PATCH 4/8] Remove Bezier and Path deprecations from 3.3. --- .../next_api_changes/removals/20465-ES.rst | 8 ++++++ lib/matplotlib/bezier.py | 25 ------------------- lib/matplotlib/path.py | 3 +-- 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index 4d81afe7d5f1..139d5f87972e 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -45,3 +45,11 @@ Artist-specific removals have been removed, for consistency with ``AxesImage.is_grayscale``. (Note that previously, these attributes were only available *after rendering the image*). + +Path helpers +~~~~~~~~~~~~ +* ``bezier.make_path_regular``; use ``Path.cleaned()`` (or + ``Path.cleaned(curves=True)``, etc.) instead, but note that these methods add + a ``STOP`` code at the end of the path. +* ``bezier.concatenate_paths``; use ``Path.make_compound_path()`` instead. +* *quantize* parameter of `.Path.cleaned()` diff --git a/lib/matplotlib/bezier.py b/lib/matplotlib/bezier.py index 32889edc8fbd..de9e340d64ab 100644 --- a/lib/matplotlib/bezier.py +++ b/lib/matplotlib/bezier.py @@ -593,28 +593,3 @@ def make_wedged_bezier2(bezier2, width, w1=1., wm=0.5, w2=0.): c3x_right, c3y_right) return path_left, path_right - - -@_api.deprecated( - "3.3", alternative="Path.cleaned() and remove the final STOP if needed") -def make_path_regular(p): - """ - If the ``codes`` attribute of `.Path` *p* is None, return a copy of *p* - with ``codes`` set to (MOVETO, LINETO, LINETO, ..., LINETO); otherwise - return *p* itself. - """ - from .path import Path - c = p.codes - if c is None: - c = np.full(len(p.vertices), Path.LINETO, dtype=Path.code_type) - c[0] = Path.MOVETO - return Path(p.vertices, c) - else: - return p - - -@_api.deprecated("3.3", alternative="Path.make_compound_path()") -def concatenate_paths(paths): - """Concatenate a list of paths into a single path.""" - from .path import Path - return Path.make_compound_path(*paths) diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 0437a7ab5577..bfc3250df647 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -458,9 +458,8 @@ def iter_bezier(self, **kwargs): raise ValueError("Invalid Path.code_type: " + str(code)) prev_vert = verts[-2:] - @_api.delete_parameter("3.3", "quantize") def cleaned(self, transform=None, remove_nans=False, clip=None, - quantize=False, simplify=False, curves=False, + *, simplify=False, curves=False, stroke_width=1.0, snap=False, sketch=None): """ Return a new Path with vertices and codes cleaned according to the From bb978a4d65b5c88fcf02e5e2ff12b0ae0ec31eb1 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 03:01:31 -0400 Subject: [PATCH 5/8] Remove transforms API deprecated in 3.3. --- doc/api/next_api_changes/removals/20465-ES.rst | 6 ++++++ doc/api/prev_api_changes/api_changes_0.98.0.rst | 4 ++-- lib/matplotlib/transforms.py | 8 -------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index 139d5f87972e..e7fe69d5e821 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -53,3 +53,9 @@ Path helpers a ``STOP`` code at the end of the path. * ``bezier.concatenate_paths``; use ``Path.make_compound_path()`` instead. * *quantize* parameter of `.Path.cleaned()` + +``BboxBase.inverse_transformed`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``.BboxBase.inverse_transformed`` has been removed (call `.BboxBase.transformed` +on the `~.Transform.inverted()` transform instead). diff --git a/doc/api/prev_api_changes/api_changes_0.98.0.rst b/doc/api/prev_api_changes/api_changes_0.98.0.rst index 0c73628e1651..c50b98cbab16 100644 --- a/doc/api/prev_api_changes/api_changes_0.98.0.rst +++ b/doc/api/prev_api_changes/api_changes_0.98.0.rst @@ -126,8 +126,8 @@ with a verb in the present tense. | ``lbwh_to_bbox(l, b, w, h)`` | `Bbox.from_bounds(x0, y0, w, h) <.Bbox.from_bounds>` | | | [It is a staticmethod.] | +--------------------------------------------+------------------------------------------------------+ -| ``inverse_transform_bbox(trans, bbox)`` | `Bbox.inverse_transformed(trans) | -| | <.BboxBase.inverse_transformed>` | +| ``inverse_transform_bbox(trans, bbox)`` | ``bbox.inverse_transformed(trans)`` | +| | | +--------------------------------------------+------------------------------------------------------+ | ``Interval.contains_open(v)`` | `interval_contains_open(tuple, v) | | | <.interval_contains_open>` | diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index aa726851ab8f..5eb3f380b5f9 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -493,14 +493,6 @@ def transformed(self, transform): [pts[0], [pts[0, 0], pts[1, 1]], [pts[1, 0], pts[0, 1]]])) return Bbox([ll, [lr[0], ul[1]]]) - @_api.deprecated("3.3", alternative="transformed(transform.inverted())") - def inverse_transformed(self, transform): - """ - Construct a `Bbox` by statically transforming this one by the inverse - of *transform*. - """ - return self.transformed(transform.inverted()) - coefs = {'C': (0.5, 0.5), 'SW': (0, 0), 'S': (0.5, 0), From aec7bb432aa42f6ac4f26d61a1e15df6ff2b1111 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 03:06:22 -0400 Subject: [PATCH 6/8] Remove testing and doc API deprecated in 3.3. --- .../next_api_changes/removals/20465-ES.rst | 13 ++++++++++++ lib/matplotlib/__init__.py | 7 +------ lib/matplotlib/docstring.py | 17 --------------- lib/matplotlib/testing/compare.py | 21 +------------------ 4 files changed, 15 insertions(+), 43 deletions(-) diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index e7fe69d5e821..c5b41ac165b1 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -59,3 +59,16 @@ Path helpers ``.BboxBase.inverse_transformed`` has been removed (call `.BboxBase.transformed` on the `~.Transform.inverted()` transform instead). + +``matplotlib.test(recursionlimit=...)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The *recursionlimit* parameter of `matplotlib.test` has been removed. + +``testing.compare.make_external_conversion_command`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... has been removed. + +``docstring.Substitution.from_params`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method has been removed. If needed, directly assign to the ``params`` +attribute of the Substitution object. diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 7bd5ed2410ea..715bd9b4015e 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1174,8 +1174,7 @@ def _init_tests(): "" if ft2font.__freetype_build_type__ == 'local' else "not ")) -@_api.delete_parameter("3.3", "recursionlimit") -def test(verbosity=None, coverage=False, *, recursionlimit=0, **kwargs): +def test(verbosity=None, coverage=False, **kwargs): """Run the matplotlib test suite.""" try: @@ -1192,8 +1191,6 @@ def test(verbosity=None, coverage=False, *, recursionlimit=0, **kwargs): old_recursionlimit = sys.getrecursionlimit() try: use('agg') - if recursionlimit: - sys.setrecursionlimit(recursionlimit) args = kwargs.pop('argv', []) provide_default_modules = True @@ -1222,8 +1219,6 @@ def test(verbosity=None, coverage=False, *, recursionlimit=0, **kwargs): finally: if old_backend.lower() != 'agg': use(old_backend) - if recursionlimit: - sys.setrecursionlimit(old_recursionlimit) return retcode diff --git a/lib/matplotlib/docstring.py b/lib/matplotlib/docstring.py index 2ae79c9b2f36..e1eaca32349e 100644 --- a/lib/matplotlib/docstring.py +++ b/lib/matplotlib/docstring.py @@ -1,7 +1,5 @@ import inspect -from matplotlib import _api - class Substitution: """ @@ -46,21 +44,6 @@ def update(self, *args, **kwargs): """ self.params.update(*args, **kwargs) - @classmethod - @_api.deprecated("3.3", alternative="assign to the params attribute") - def from_params(cls, params): - """ - In the case where the params is a mutable sequence (list or - dictionary) and it may change before this class is called, one may - explicitly use a reference to the params rather than using *args or - **kwargs which will copy the values and not reference them. - - :meta private: - """ - result = cls() - result.params = params - return result - def _recursive_subclasses(cls): yield cls diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 242bab25cd53..ec4e9132bbbf 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -19,7 +19,7 @@ from PIL import Image import matplotlib as mpl -from matplotlib import _api, cbook +from matplotlib import cbook from matplotlib.testing.exceptions import ImageComparisonFailure _log = logging.getLogger(__name__) @@ -64,25 +64,6 @@ def get_file_hash(path, block_size=2 ** 20): return md5.hexdigest() -@_api.deprecated("3.3") -def make_external_conversion_command(cmd): - def convert(old, new): - cmdline = cmd(old, new) - pipe = subprocess.Popen(cmdline, universal_newlines=True, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = pipe.communicate() - errcode = pipe.wait() - if not os.path.exists(new) or errcode: - msg = "Conversion command failed:\n%s\n" % ' '.join(cmdline) - if stdout: - msg += "Standard output:\n%s\n" % stdout - if stderr: - msg += "Standard error:\n%s\n" % stderr - raise IOError(msg) - - return convert - - # Modified from https://bugs.python.org/issue25567. _find_unsafe_bytes = re.compile(br'[^a-zA-Z0-9_@%+=:,./-]').search From 19da8c3ecd6657dc48d8cb687499db305fdafa30 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 03:15:42 -0400 Subject: [PATCH 7/8] Remove widget API deprecated in 3.3. --- .../next_api_changes/removals/20465-ES.rst | 13 +++++ lib/matplotlib/widgets.py | 51 ------------------- 2 files changed, 13 insertions(+), 51 deletions(-) diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index c5b41ac165b1..ac5837720fd3 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -72,3 +72,16 @@ The *recursionlimit* parameter of `matplotlib.test` has been removed. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This method has been removed. If needed, directly assign to the ``params`` attribute of the Substitution object. + +``widgets.TextBox.params_to_disable`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This attribute has been removed. + +`.widgets.SubplotTool` callbacks and axes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``funcleft``, ``funcright``, ``funcbottom``, ``functop``, ``funcwspace``, +and ``funchspace`` methods of `.widgets.SubplotTool` have been removed. + +The ``axleft``, ``axright``, ``axbottom``, ``axtop``, ``axwspace``, and +``axhspace`` attributes of `.widgets.SubplotTool` have been removed. Access +the ``ax`` attribute of the corresponding slider, if needed. diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 58a88263a90e..0b3067157e2e 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -984,8 +984,6 @@ class TextBox(AxesWidget): The color of the text box when hovering. """ - params_to_disable = _api.deprecated("3.3")(property( - lambda self: [key for key in mpl.rcParams if 'keymap' in key])) cnt = _api.deprecated("3.4")(property( # Not real, but close enough. lambda self: sum(len(d) for d in self._observers.callbacks.values()))) change_observers = _api.deprecated("3.4")(property( @@ -1430,55 +1428,6 @@ def _on_reset(self, event): event.canvas.draw() # Redraw the subplottool canvas. self._on_slider_changed(None) # Apply changes to the target window. - axleft = _api.deprecated("3.3")( - property(lambda self: self.sliderleft.ax)) - axright = _api.deprecated("3.3")( - property(lambda self: self.sliderright.ax)) - axbottom = _api.deprecated("3.3")( - property(lambda self: self.sliderbottom.ax)) - axtop = _api.deprecated("3.3")( - property(lambda self: self.slidertop.ax)) - axwspace = _api.deprecated("3.3")( - property(lambda self: self.sliderwspace.ax)) - axhspace = _api.deprecated("3.3")( - property(lambda self: self.sliderhspace.ax)) - - @_api.deprecated("3.3") - def funcleft(self, val): - self.targetfig.subplots_adjust(left=val) - if self.drawon: - self.targetfig.canvas.draw() - - @_api.deprecated("3.3") - def funcright(self, val): - self.targetfig.subplots_adjust(right=val) - if self.drawon: - self.targetfig.canvas.draw() - - @_api.deprecated("3.3") - def funcbottom(self, val): - self.targetfig.subplots_adjust(bottom=val) - if self.drawon: - self.targetfig.canvas.draw() - - @_api.deprecated("3.3") - def functop(self, val): - self.targetfig.subplots_adjust(top=val) - if self.drawon: - self.targetfig.canvas.draw() - - @_api.deprecated("3.3") - def funcwspace(self, val): - self.targetfig.subplots_adjust(wspace=val) - if self.drawon: - self.targetfig.canvas.draw() - - @_api.deprecated("3.3") - def funchspace(self, val): - self.targetfig.subplots_adjust(hspace=val) - if self.drawon: - self.targetfig.canvas.draw() - class Cursor(AxesWidget): """ From b04f1a31fdce0bcf718e46fcd367e8120c1d4d5d Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 19 Jun 2021 03:18:28 -0400 Subject: [PATCH 8/8] Remove ToolManager API deprecated in 3.3. --- .../next_api_changes/removals/20465-ES.rst | 6 ++++ lib/matplotlib/backend_managers.py | 28 ++++++------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/doc/api/next_api_changes/removals/20465-ES.rst b/doc/api/next_api_changes/removals/20465-ES.rst index ac5837720fd3..abe773d5b2a2 100644 --- a/doc/api/next_api_changes/removals/20465-ES.rst +++ b/doc/api/next_api_changes/removals/20465-ES.rst @@ -85,3 +85,9 @@ and ``funchspace`` methods of `.widgets.SubplotTool` have been removed. The ``axleft``, ``axright``, ``axbottom``, ``axtop``, ``axwspace``, and ``axhspace`` attributes of `.widgets.SubplotTool` have been removed. Access the ``ax`` attribute of the corresponding slider, if needed. + +Variants on ``ToolManager.update_keymap`` calls +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Passing multiple keys as a single comma-separated string or multiple arguments +to `.ToolManager.update_keymap` is no longer supported; pass keys as a list of +strings instead. diff --git a/lib/matplotlib/backend_managers.py b/lib/matplotlib/backend_managers.py index cd9efb62c7a0..a8d493aea177 100644 --- a/lib/matplotlib/backend_managers.py +++ b/lib/matplotlib/backend_managers.py @@ -1,5 +1,4 @@ from matplotlib import _api, cbook, widgets -from matplotlib.rcsetup import validate_stringlist import matplotlib.backend_tools as tools @@ -175,8 +174,7 @@ def _remove_keys(self, name): for k in self.get_tool_keymap(name): del self._keys[k] - @_api.delete_parameter("3.3", "args") - def update_keymap(self, name, key, *args): + def update_keymap(self, name, key): """ Set the keymap to associate with the specified tool. @@ -188,23 +186,15 @@ def update_keymap(self, name, key, *args): Keys to associate with the tool. """ if name not in self._tools: - raise KeyError('%s not in Tools' % name) + raise KeyError(f'{name} not in Tools') self._remove_keys(name) - for key in [key, *args]: - if isinstance(key, str) and validate_stringlist(key) != [key]: - _api.warn_deprecated( - "3.3", message="Passing a list of keys as a single " - "comma-separated string is deprecated since %(since)s and " - "support will be removed %(removal)s; pass keys as a list " - "of strings instead.") - key = validate_stringlist(key) - if isinstance(key, str): - key = [key] - for k in key: - if k in self._keys: - _api.warn_external( - f'Key {k} changed from {self._keys[k]} to {name}') - self._keys[k] = name + if isinstance(key, str): + key = [key] + for k in key: + if k in self._keys: + _api.warn_external( + f'Key {k} changed from {self._keys[k]} to {name}') + self._keys[k] = name def remove_tool(self, name): """