Skip to content

Commit a17ff6c

Browse files
committed
Decorator for deleting a parameter with a deprecation period.
As an example application, deprecate the unused shape and imlim args to imshow() (unused since around 4d1107c (2006)).
1 parent c8b396c commit a17ff6c

File tree

7 files changed

+85
-6
lines changed

7 files changed

+85
-6
lines changed

doc/api/next_api_changes/2018-01-10-AL.rst

+5
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ Changes in parameter names
99
In each case, the old argument name remains supported (it cannot be used
1010
simultaneously with the new name), but suppport for it will be dropped in
1111
Matplotlib 3.3.
12+
13+
- The unused ``shape`` and ``imlim`` parameters to `Axes.imshow` are
14+
deprecated. To avoid triggering the deprecation warning, the ``filternorm``,
15+
``filterrad``, ``resample``, and ``url`` arguments should be passed by
16+
keyword.

lib/matplotlib/axes/_axes.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -5328,6 +5328,8 @@ def get_interp_point(ind):
53285328

53295329
#### plotting z(x,y): imshow, pcolor and relatives, contour
53305330
@_preprocess_data()
5331+
@cbook._delete_parameter("3.1", "shape")
5332+
@cbook._delete_parameter("3.1", "imlim")
53315333
def imshow(self, X, cmap=None, norm=None, aspect=None,
53325334
interpolation=None, alpha=None, vmin=None, vmax=None,
53335335
origin=None, extent=None, shape=None, filternorm=1,
@@ -5444,9 +5446,6 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
54445446
See the example :doc:`/tutorials/intermediate/imshow_extent` for a
54455447
more detailed description.
54465448
5447-
shape : scalars (columns, rows), optional, default: None
5448-
For raw buffer images.
5449-
54505449
filternorm : bool, optional, default: True
54515450
A parameter for the antigrain image resize filter (see the
54525451
antigrain documentation). If *filternorm* is set, the filter

lib/matplotlib/cbook/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
import matplotlib
3434
from .deprecation import (
35-
deprecated, warn_deprecated, _rename_parameter,
35+
deprecated, warn_deprecated, _rename_parameter, _delete_parameter,
3636
MatplotlibDeprecationWarning, mplDeprecation)
3737

3838

lib/matplotlib/cbook/deprecation.py

+57
Original file line numberDiff line numberDiff line change
@@ -306,3 +306,60 @@ def wrapper(*args, **kwargs):
306306
# pyplot would explicitly pass both arguments to the Axes method.
307307

308308
return wrapper
309+
310+
311+
class _deprecated_parameter_class:
312+
def __repr__(self):
313+
return "<deprecated parameter>"
314+
315+
316+
_deprecated_parameter = _deprecated_parameter_class()
317+
318+
319+
def _delete_parameter(since, name, func=None):
320+
"""
321+
Decorator indicating that parameter *name* of *func* is being deprecated.
322+
323+
The actual implementation of *func* should keep the *name* parameter in its
324+
signature.
325+
326+
Parameters that come after the deprecated parameter effectively become
327+
keyword-only (as they cannot be passed positionally without triggering the
328+
DeprecationWarning on the deprecated parameter), and should be marked as
329+
such after the deprecation period has passed and the deprecated parameter
330+
is removed.
331+
332+
Examples
333+
--------
334+
335+
::
336+
@_delete_parameter("3.1", "unused")
337+
def func(used_arg, other_arg, unused, more_args): ...
338+
"""
339+
340+
if func is None:
341+
return functools.partial(_delete_parameter, since, name)
342+
343+
signature = inspect.signature(func)
344+
assert name in signature.parameters, (
345+
f"Matplotlib internal error: {name!r} must be a parameter for "
346+
f"{func.__name__}()")
347+
func.__signature__ = signature.replace(parameters=[
348+
param.replace(default=_deprecated_parameter) if param.name == name
349+
else param
350+
for param in signature.parameters.values()])
351+
352+
@functools.wraps(func)
353+
def wrapper(*args, **kwargs):
354+
arguments = func.__signature__.bind(*args, **kwargs).arguments
355+
# We cannot just check `name not in arguments` because the pyplot
356+
# wrappers always pass all arguments explicitly.
357+
if name in arguments and arguments[name] != _deprecated_parameter:
358+
warn_deprecated(
359+
since, message=f"The {name!r} parameter of {func.__name__}() "
360+
f"is deprecated since Matplotlib {since} and will be removed "
361+
f"%(removal)s. If any parameter follows {name!r}, they "
362+
f"should be pass as keyword, not positionally.")
363+
return func(*args, **kwargs)
364+
365+
return wrapper

lib/matplotlib/pyplot.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2657,7 +2657,8 @@ def hlines(
26572657
def imshow(
26582658
X, cmap=None, norm=None, aspect=None, interpolation=None,
26592659
alpha=None, vmin=None, vmax=None, origin=None, extent=None,
2660-
shape=None, filternorm=1, filterrad=4.0, imlim=None,
2660+
shape=cbook.deprecation._deprecated_parameter, filternorm=1,
2661+
filterrad=4.0, imlim=cbook.deprecation._deprecated_parameter,
26612662
resample=None, url=None, *, data=None, **kwargs):
26622663
__ret = gca().imshow(
26632664
X, cmap=cmap, norm=norm, aspect=aspect,

lib/matplotlib/tests/test_image.py

+15
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from matplotlib import (
1616
colors, image as mimage, patches, pyplot as plt,
1717
rc_context, rcParams)
18+
from matplotlib.cbook import MatplotlibDeprecationWarning
1819
from matplotlib.image import (AxesImage, BboxImage, FigureImage,
1920
NonUniformImage, PcolorImage)
2021
from matplotlib.testing.decorators import image_comparison
@@ -936,3 +937,17 @@ def test_relim():
936937
ax.relim()
937938
ax.autoscale()
938939
assert ax.get_xlim() == ax.get_ylim() == (0, 1)
940+
941+
942+
def test_deprecation():
943+
data = [[1, 2], [3, 4]]
944+
ax = plt.figure().subplots()
945+
for obj in [ax, plt]:
946+
with pytest.warns(None) as record:
947+
obj.imshow(data)
948+
assert len(record) == 0
949+
with pytest.warns(MatplotlibDeprecationWarning):
950+
obj.imshow(data, shape=None)
951+
with pytest.warns(MatplotlibDeprecationWarning):
952+
# Enough arguments to pass "shape" positionally.
953+
obj.imshow(data, *[None] * 10)

tools/boilerplate.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
# This line imports the installed copy of matplotlib, and not the local copy.
2222
import numpy as np
23-
from matplotlib import mlab
23+
from matplotlib import cbook, mlab
2424
from matplotlib.axes import Axes
2525

2626

@@ -175,6 +175,8 @@ def __init__(self, value):
175175
self._repr = "mlab.window_hanning"
176176
elif value is np.mean:
177177
self._repr = "np.mean"
178+
elif value is cbook.deprecation._deprecated_parameter:
179+
self._repr = "cbook.deprecation._deprecated_parameter"
178180
else:
179181
self._repr = repr(value)
180182

0 commit comments

Comments
 (0)