Skip to content

Commit cb770b8

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 114d516 commit cb770b8

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 parameter 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
@@ -5392,6 +5392,8 @@ def get_interp_point(ind):
53925392

53935393
#### plotting z(x,y): imshow, pcolor and relatives, contour
53945394
@_preprocess_data()
5395+
@cbook._delete_parameter("3.1", "shape")
5396+
@cbook._delete_parameter("3.1", "imlim")
53955397
def imshow(self, X, cmap=None, norm=None, aspect=None,
53965398
interpolation=None, alpha=None, vmin=None, vmax=None,
53975399
origin=None, extent=None, shape=None, filternorm=1,
@@ -5508,9 +5510,6 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
55085510
See the example :doc:`/tutorials/intermediate/imshow_extent` for a
55095511
more detailed description.
55105512
5511-
shape : scalars (columns, rows), optional, default: None
5512-
For raw buffer images.
5513-
55145513
filternorm : bool, optional, default: True
55155514
A parameter for the antigrain image resize filter (see the
55165515
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
@@ -2658,7 +2658,8 @@ def hlines(
26582658
def imshow(
26592659
X, cmap=None, norm=None, aspect=None, interpolation=None,
26602660
alpha=None, vmin=None, vmax=None, origin=None, extent=None,
2661-
shape=None, filternorm=1, filterrad=4.0, imlim=None,
2661+
shape=cbook.deprecation._deprecated_parameter, filternorm=1,
2662+
filterrad=4.0, imlim=cbook.deprecation._deprecated_parameter,
26622663
resample=None, url=None, *, data=None, **kwargs):
26632664
__ret = gca().imshow(
26642665
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
@@ -944,3 +945,17 @@ def test_relim():
944945
ax.relim()
945946
ax.autoscale()
946947
assert ax.get_xlim() == ax.get_ylim() == (0, 1)
948+
949+
950+
def test_deprecation():
951+
data = [[1, 2], [3, 4]]
952+
ax = plt.figure().subplots()
953+
for obj in [ax, plt]:
954+
with pytest.warns(None) as record:
955+
obj.imshow(data)
956+
assert len(record) == 0
957+
with pytest.warns(MatplotlibDeprecationWarning):
958+
obj.imshow(data, shape=None)
959+
with pytest.warns(MatplotlibDeprecationWarning):
960+
# Enough arguments to pass "shape" positionally.
961+
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)