8000 Deprecate JPEG-specific kwargs and rcParams to savefig. by anntzer · Pull Request #16231 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Deprecate JPEG-specific kwargs and rcParams to savefig. #16231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion doc/api/next_api_changes/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ The following validators, defined in `.rcsetup`, are deprecated:
``validate_movie_frame_fmt``, ``validate_axis_locator``,
``validate_movie_html_fmt``, ``validate_grid_axis``,
``validate_axes_titlelocation``, ``validate_toolbar``,
<<<<<<< HEAD
``validate_ps_papersize``, ``validate_legend_loc``,
``validate_bool_maybe_none``, ``validate_hinting``,
``validate_movie_writers``.
Expand Down Expand Up @@ -299,3 +298,12 @@ is deprecated, set the offset to 0 instead.
``autofmt_xdate(which=None)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is deprecated, use its more explicit synonym, ``which="major"``, instead.

JPEG options
~~~~~~~~~~~~
The *quality*, *optimize*, and *progressive* keyword arguments to
`~.Figure.savefig`, which were only used when saving to JPEG, are deprecated.
:rc:`savefig.jpeg_quality` is likewise deprecated.

Such options should now be directly passed to Pillow using
``savefig(..., pil_kwargs={"quality": ..., "optimize": ..., "progressive": ...})``.
1 change: 1 addition & 0 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ def gen_candidates():
'animation.avconv_args': ('3.3',),
'mathtext.fallback_to_cm': ('3.3',),
'keymap.all_axes': ('3.3',),
'savefig.jpeg_quality': ('3.3',),
}


Expand Down
24 changes: 21 additions & 3 deletions lib/matplotlib/backends/backend_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,12 @@ def print_to_buffer(self):
# matches the dpi kwarg (if any).

@cbook._delete_parameter("3.2", "dryrun")
@cbook._delete_parameter("3.3", "quality",
alternative="pil_kwargs={'quality': ...}")
@cbook._delete_parameter("3.3", "optimize",
alternative="pil_kwargs={'optimize': ...}")
@cbook._delete_parameter("3.3", "progressive",
alternative="pil_kwargs={'progressive': ...}")
def print_jpg(self, filename_or_obj, *args, dryrun=False, pil_kwargs=None,
**kwargs):
"""
Expand All @@ -528,14 +534,17 @@ def print_jpg(self, filename_or_obj, *args, dryrun=False, pil_kwargs=None,
The image quality, on a scale from 1 (worst) to 95 (best).
Values above 95 should be avoided; 100 disables portions of
the JPEG compression algorithm, and results in large files
with hardly any gain in image quality.
with hardly any gain in image quality. This parameter is
deprecated.

optimize : bool, default: False
Whether the encoder should make an extra pass over the image
in order to select optimal encoder settings.
in order to select optimal encoder settings. This parameter is
deprecated.

progressive : bool, default: False
Whether the image should be stored as a progressive JPEG file.
This parameter is deprecated.

pil_kwargs : dict, optional
Additional keyword arguments that are passed to
Expand All @@ -555,7 +564,16 @@ def print_jpg(self, filename_or_obj, *args, dryrun=False, pil_kwargs=None,
for k in ["quality", "optimize", "progressive"]:
if k in kwargs:
pil_kwargs.setdefault(k, kwargs[k])
pil_kwargs.setdefault("quality", mpl.rcParams["savefig.jpeg_quality"])
if "quality" not in pil_kwargs:
quality = pil_kwargs["quality"] = \
dict.__getitem__(mpl.rcParams, "savefig.jpeg_quality")
if quality not in [0, 75, 95]: # default qualities.
cbook.warn_deprecated(
"3.3", name="savefig.jpeg_quality", obj_type="rcParam",
addendum="Set the quality using "
"`pil_kwargs={'quality': ...}`; the future default "
"quality will be 75, matching the default of Pillow and "
"libjpeg.")
pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi))
return background.save(
filename_or_obj, format='jpeg', **pil_kwargs)
Expand Down
5 changes: 3 additions & 2 deletions lib/matplotlib/backends/backend_wx.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,9 @@ def _print_image(self, filename, filetype, *args, **kwargs):
# are saving a JPEG, convert the wx.Bitmap to a wx.Image,
# and set the quality.
if filetype == wx.BITMAP_TYPE_JPEG:
jpeg_quality = kwargs.get('quality',
mpl.rcParams['savefig.jpeg_quality'])
jpeg_quality = kwargs.get(
'quality',
dict.__getitem__(mpl.rcParams, 'savefig.jpeg_quality'))
image = self.bitmap.ConvertToImage()
image.SetOption(wx.IMAGE_OPTION_QUALITY, str(jpeg_quality))

Expand Down
47 changes: 31 additions & 16 deletions lib/matplotlib/cbook/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,17 @@ def _delete_parameter(since, name, func=None, **kwargs):
Decorator indicating that parameter *name* of *func* is being deprecated.

The actual implementation of *func* should keep the *name* parameter in its
signature.
signature, or accept a ``**kwargs`` argument (through which *name* would be
passed).

Parameters that come after the deprecated parameter effectively become
keyword-only (as they cannot be passed positionally without triggering the
DeprecationWarning on the deprecated parameter), and should be marked as
such after the deprecation period has passed and the deprecated parameter
is removed.

Additional keyword arguments are passed to `.warn_deprecated`.
Parameters other than *since*, *name*, and *func* are keyword-only and
forwarded to `.warn_deprecated`.

Examples
--------
Expand All @@ -334,17 +336,25 @@ def func(used_arg, other_arg, unused, more_args): ...
return functools.partial(_delete_parameter, since, name, **kwargs)

signature = inspect.signature(func)
assert name in signature.parameters, (
f"Matplotlib internal error: {name!r} must be a parameter for "
f"{func.__name__}()")
kind = signature.parameters[name].kind
is_varargs = kind is inspect.Parameter.VAR_POSITIONAL
is_varkwargs = kind is inspect.Parameter.VAR_KEYWORD
if not is_varargs and not is_varkwargs:
func.__signature__ = signature = signature.replace(parameters=[
param.replace(default=_deprecated_parameter) if param.name == name
else param
for param in signature.parameters.values()])
# Name of `**kwargs` parameter of the decorated function, typically
# "kwargs" if such a parameter exists, or None if the decorated function
# doesn't accept `**kwargs`.
kwargs_name = next((param.name for param in signature.parameters.values()
if param.kind == inspect.Parameter.VAR_KEYWORD), None)
if name in signature.parameters:
kind = signature.parameters[name].kind
is_varargs = kind is inspect.Parameter.VAR_POSITIONAL
is_varkwargs = kind is inspect.Parameter.VAR_KEYWORD
if not is_varargs and not is_varkwargs:
func.__signature__ = signature = signature.replace(parameters=[
param.replace(default=_deprecated_parameter)
if param.name == name else param
for param in signature.parameters.values()])
else:
is_varargs = is_varkwargs = False
assert kwargs_name, (
f"Matplotlib internal error: {name!r} must be a parameter for "
f"{func.__name__}()")

@functools.wraps(func)
def wrapper(*inner_args, **inner_kwargs):
Expand All @@ -361,13 +371,18 @@ def wrapper(*inner_args, **inner_kwargs):
f"support for them will be removed %(removal)s.")
# We cannot just check `name not in arguments` because the pyplot
# wrappers always pass all arguments explicitly.
elif name in arguments and arguments[name] != _deprecated_parameter:
elif any(name in d and d[name] != _deprecated_parameter
for d in [arguments, arguments.get(kwargs_name, {})]):
addendum = (f"If any parameter follows {name!r}, they should be "
f"passed as keyword, not positionally.")
if kwargs.get("addendum"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICS this is a new optional parameter of _delete_parameter. Please make it explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now mentioned that all additional parameters are forwarded to warn_deprecated.

kwargs["addendum"] += " " + addendum
else:
kwargs["addendum"] = addendum
warn_deprecated(
since,
name=repr(name),
obj_type=f"parameter of {func.__name__}()",
addendum=(f"If any parameter follows {name!r}, they should be "
f"passed as keyword, not positionally."),
**kwargs)
return func(*inner_args, **inner_kwargs)

Expand Down
6 changes: 6 additions & 0 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2065,17 +2065,23 @@ def savefig(self, fname, *, transparent=None, **kwargs):
the JPEG compression algorithm, and results in large files
with hardly any gain in image quality.

This parameter is deprecated.

optimize : bool, default: False
Applicable only if *format* is 'jpg' or 'jpeg', ignored otherwise.

Whether the encoder should make an extra pass over the image
in order to select optimal encoder settings.

This parameter is deprecated.

progressive : bool, default: False
Applicable only if *format* is 'jpg' or 'jpeg', ignored otherwise.

Whether the image should be stored as a progressive JPEG file.

This parameter is deprecated.

facecolor : color, default: :rc:`savefig.facecolor`
The facecolor of the figure.

Expand Down
1 change: 0 additions & 1 deletion lib/matplotlib/mpl-data/stylelib/classic.mplstyle
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,6 @@ savefig.bbox : standard # 'tight' or 'standard'.
# e.g. setting animation.writer to ffmpeg will not work,
# use ffmpeg_file instead
savefig.pad_inches : 0.1 # Padding to be used when bbox is set to 'tight'
savefig.jpeg_quality: 95 # when a jpeg is saved, the default quality parameter.
savefig.transparent : False # setting that controls whether figures are saved with a
# transparent background by default
savefig.orientation : portrait
Expand Down
22 changes: 22 additions & 0 deletions lib/matplotlib/tests/test_cbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,28 @@ def test_safe_first_element_pandas_series(pd):
assert actual == 0


def test_delete_parameter():
@cbook._delete_parameter("3.0", "foo")
def func1(foo=None):
pass

@cbook._delete_parameter("3.0", "foo")
def func2(**kwargs):
pass

for func in [func1, func2]:
func() # No warning.
with pytest.warns(MatplotlibDeprecationWarning):
func(foo="bar")

def pyplot_wrapper(foo=cbook.deprecation._deprecated_parameter):
func1(foo)

pyplot_wrapper() # No warning.
with pytest.warns(MatplotlibDeprecationWarning):
func(foo="bar")


def test_make_keyword_only():
@cbook._make_keyword_only("3.0", "arg")
def func(pre, arg, post=None):
Expand Down
1 change: 0 additions & 1 deletion matplotlibrc.template
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,6 @@
# e.g. setting animation.writer to ffmpeg will not work,
# use ffmpeg_file instead
#savefig.pad_inches: 0.1 # Padding to be used when bbox is set to 'tight'
#savefig.jpeg_quality: 95 # when a jpeg is saved, the default quality parameter.
#savefig.directory: ~ # default directory in savefig dialog box,
# leave empty to always use current working directory
#savefig.transparent: False # setting that controls whether figures are saved with a
Expand Down
0