8000 Deprecate JPEG-specific kwargs and rcParams to savefig. · matplotlib/matplotlib@d75769f · GitHub
[go: up one dir, main page]

Skip to content

Commit d75769f

Browse files
committed
Deprecate JPEG-specific kwargs and rcParams to savefig.
Saving Matplotlib figures to jpeg is generally not a great idea to start with (even at the default quality of 95 there are visible (faint) artefacts around sharp lines, and at quality=95 the files produced are bigger than the corresponding pngs anyways). We don't need to completely get rid of jpeg support, but we can at least simplify the code path (which otherwise also needs to be duplicated in mplcairo) and not have to document these jpeg-specific kwargs (in two places!). Note that users can still set them via `pil_kwargs`. The changes to _delete_parameter are so that we can write ``` @_delete_parameter(..., "foo") def f(**kwargs): ... ``` where `foo` actually only shows up in `kwargs`.
1 parent f3862ef commit d75769f

File tree

8 files changed

+71
-23
lines changed

8 files changed

+71
-23
lines changed

doc/api/next_api_changes/deprecations.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,12 @@ is deprecated, set the offset to 0 instead.
282282
``RendererCairo.fontweights``, ``RendererCairo.fontangles``
283283
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284284
... are deprecated.
285+
286+
JPEG options
287+
~~~~~~~~~~~~
288+
The *quality*, *optimize*, and *progressive* keyword arguments to
289+
`~.Figure.savefig`, which were only used when saving to JPEG, are deprecated.
290+
:rc:`savefig.jpeg_quality` is likewise deprecated.
291+
292+
Such options should now be directly passed to Pillow using
293+
``savefig(..., pil_kwargs={"quality": ..., "optimize": ..., "progressive": ...})``.

lib/matplotlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ def gen_candidates():
604604
'animation.avconv_args': ('3.3',),
605605
'mathtext.fallback_to_cm': ('3.3',),
606606
'keymap.all_axes': ('3.3',),
607+
'savefig.jpeg_quality': ('3.3',),
607608
}
608609

609610

lib/matplotlib/backends/backend_agg.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,12 @@ def print_to_buffer(self):
512512
# matches the dpi kwarg (if any).
513513

514514
@cbook._delete_parameter("3.2", "dryrun")
515+
@cbook._delete_parameter("3.3", "quality",
516+
alternative="pil_kwargs={'quality': ...}")
517+
@cbook._delete_parameter("3.3", "optimize",
518+
alternative="pil_kwargs={'optimize': ...}")
519+
@cbook._delete_parameter("3.3", "progressive",
520+
alternative="pil_kwargs={'progressive': ...}")
515521
def print_jpg(self, filename_or_obj, *args, dryrun=False, pil_kwargs=None,
516522
**kwargs):
517523
"""
@@ -528,14 +534,17 @@ def print_jpg(self, filename_or_obj, *args, dryrun=False, pil_kwargs=None,
528534
The image quality, on a scale from 1 (worst) to 95 (best).
529535
Values above 95 should be avoided; 100 disables portions of
530536
the JPEG compression algorithm, and results in large files
531-
with hardly any gain in image quality.
537+
with hardly any gain in image quality. This parameter is
538+
deprecated.
532539
533540
optimize : bool, default: False
534541
Whether the encoder should make an extra pass over the image
535-
in order to select optimal encoder settings.
542+
in order to select optimal encoder settings. This parameter is
543+
deprecated.
536544
537545
progressive : bool, default: False
538546
Whether the image should be stored as a progressive JPEG file.
547+
This parameter is deprecated.
539548
540549
pil_kwargs : dict, optional
541550
Additional keyword arguments that are passed to
@@ -555,7 +564,16 @@ def print_jpg(self, filename_or_obj, *args, dryrun=False, pil_kwargs=None,
555564
for k in ["quality", "optimize", "progressive"]:
556565
if k in kwargs:
557566
pil_kwargs.setdefault(k, kwargs[k])
558-
pil_kwargs.setdefault("quality", mpl.rcParams["savefig.jpeg_quality"])
567+
if "quality" not in pil_kwargs:
568+
quality = pil_kwargs["quality"] = \
569+
dict.__getitem__(mpl.rcParams, "savefig.jpeg_quality")
570+
if quality not in [0, 75, 95]: # default qualities.
571+
cbook.warn_deprecated(
572+
"3.3", name="savefig.jpeg_quality", obj_type="rcParam",
573+
addendum="Set the quality using "
574+
"`pil_kwargs={'quality': ...}`; the future default "
575+
"quality will be 75, matching the default of Pillow and "
576+
"libjpeg.")
559577
pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi))
560578
return background.save(
561579
filename_or_obj, format='jpeg', **pil_kwargs)

lib/matplotlib/backends/backend_wx.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,8 +885,9 @@ def _print_image(self, filename, filetype, *args, **kwargs):
885885
# are saving a JPEG, convert the wx.Bitmap to a wx.Image,
886886
# and set the quality.
887887
if filetype == wx.BITMAP_TYPE_JPEG:
888-
jpeg_quality = kwargs.get('quality',
889-
mpl.rcParams['savefig.jpeg_quality'])
888+
jpeg_quality = kwargs.get(
889+
'quality',
890+
dict.__getitem__(mpl.rcParams, 'savefig.jpeg_quality'))
890891
image = self.bitmap.ConvertToImage()
891892
image.SetOption(wx.IMAGE_OPTION_QUALITY, str(jpeg_quality))
892893

lib/matplotlib/cbook/deprecation.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -312,15 +312,17 @@ def _delete_parameter(since, name, func=None, **kwargs):
312312
Decorator indicating that parameter *name* of *func* is being deprecated.
313313
314314
The actual implementation of *func* should keep the *name* parameter in its
315-
signature.
315+
signature, or accept a ``**kwargs`` argument (through which *name* would be
316+
passed).
316317
317318
Parameters that come after the deprecated parameter effectively become
318319
keyword-only (as they cannot be passed positionally without triggering the
319320
DeprecationWarning on the deprecated parameter), and should be marked as
320321
such after the deprecation period has passed and the deprecated parameter
321322
is removed.
322323
323-
Additional keyword arguments are passed to `.warn_deprecated`.
324+
Parameters other than *since*, *name*, and *func* are keyword-only and
325+
forwarded to `.warn_deprecated`.
324326
325327
Examples
326328
--------
@@ -334,17 +336,25 @@ def func(used_arg, other_arg, unused, more_args): ...
334336
return functools.partial(_delete_parameter, since, name, **kwargs)
335337

336338
signature = inspect.signature(func)
337-
assert name in signature.parameters, (
338-
f"Matplotlib internal error: {name!r} must be a parameter for "
339-
f"{func.__name__}()")
340-
kind = signature.parameters[name].kind
341-
is_varargs = kind is inspect.Parameter.VAR_POSITIONAL
342-
is_varkwargs = kind is inspect.Parameter.VAR_KEYWORD
343-
if not is_varargs and not is_varkwargs:
344-
func.__signature__ = signature = signature.replace(parameters=[
345-
param.replace(default=_deprecated_parameter) if param.name == name
346-
else param
347-
for param in signature.parameters.values()])
339+
# Name of `**kwargs` parameter of the decorated function, typically
340+
# "kwargs" if such a parameter exists, or None if the decorated function
341+
# doesn't accept `**kwargs`.
342+
kwargs_name = next((param.name for param in signature.parameters.values()
343+
if param.kind == inspect.Parameter.VAR_KEYWORD), None)
344+
if name in signature.parameters:
345+
kind = signature.parameters[name].kind
346+
is_varargs = kind is inspect.Parameter.VAR_POSITIONAL
347+
is_varkwargs = kind is inspect.Parameter.VAR_KEYWORD
348+
if not is_varargs and not is_varkwargs:
349+
func.__signature__ = signature = signature.replace(parameters=[
350+
param.replace(default=_deprecated_parameter)
351+
if param.name == name else param
352+
for param in signature.parameters.values()])
353+
else:
354+
is_varargs = is_varkwargs = False
355+
assert kwargs_name, (
356+
f"Matplotlib internal error: {name!r} must be a parameter for "
357+
f"{func.__name__}()")
348358

349359
@functools.wraps(func)
350360
def wrapper(*inner_args, **inner_kwargs):
@@ -361,13 +371,18 @@ def wrapper(*inner_args, **inner_kwargs):
361371
f"support for them will be removed %(removal)s.")
362372
# We cannot just check `name not in arguments` because the pyplot
363373
# wrappers always pass all arguments explicitly.
364-
elif name in arguments and arguments[name] != _deprecated_parameter:
374+
elif any(name in d and d[name] != _deprecated_parameter
375+
for d in [arguments, arguments.get(kwargs_name, {})]):
376+
addendum = (f"If any parameter follows {name!r}, they should be "
377+
f"passed as keyword, not positionally.")
378+
if kwargs.get("addendum"):
379+
kwargs["addendum"] += " " + addendum
380+
else:
381+
kwargs["addendum"] = addendum
365382
warn_deprecated(
366383
since,
367384
name=repr(name),
368385
obj_type=f"parameter of {func.__name__}()",
369-
addendum=(f"If any parameter follows {name!r}, they should be "
370-
f"passed as keyword, not positionally."),
371386
**kwargs)
372387
return func(*inner_args, **inner_kwargs)
373388

lib/matplotlib/figure.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,17 +2064,23 @@ def savefig(self, fname, *, transparent=None, **kwargs):
20642064
the JPEG compression algorithm, and results in large files
20652065
with hardly any gain in image quality.
20662066
2067+
This parameter is deprecated.
2068+
20672069
optimize : bool, default: False
20682070
Applicable only if *format* is 'jpg' or 'jpeg', ignored otherwise.
20692071
20702072
Whether the encoder should make an extra pass over the image
20712073
in order to select optimal encoder settings.
20722074
2075+
This parameter is deprecated.
2076+
20732077
progressive : bool, default: False
20742078
Applicable only if *format* is 'jpg' or 'jpeg', ignored otherwise.
20752079
20762080
Whether the image should be stored as a progressive JPEG file.
20772081
2082+
This parameter is deprecated.
2083+
20782084
facecolor : color, default: :rc:`savefig.facecolor`
20792085
The facecolor of the figure.
20802086

lib/matplotlib/mpl-data/stylelib/classic.mplstyle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,6 @@ savefig.bbox : standard # 'tight' or 'standard'.
420420
# e.g. setting animation.writer to ffmpeg will not work,
421421
# use ffmpeg_file instead
422422
savefig.pad_inches : 0.1 # Padding to be used when bbox is set to 'tight'
423-
savefig.jpeg_quality: 95 # when a jpeg is saved, the default quality parameter.
424423
savefig.transparent : False # setting that controls whether figures are saved with a
425424
# transparent background by default
426425
savefig.orientation : portrait

matplotlibrc.template

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,6 @@
651651
# e.g. setting animation.writer to ffmpeg will not work,
652652
# use ffmpeg_file instead
653653
#savefig.pad_inches: 0.1 # Padding to be used when bbox is set to 'tight'
654-
#savefig.jpeg_quality: 95 # when a jpeg is saved, the default quality parameter.
655654
#savefig.directory: ~ # default directory in savefig dialog box,
656655
# leave empty to always use current working directory
657656
#savefig.transparent: False # setting that controls whether figures are saved with a

0 commit comments

Comments
 (0)
0