Skip to content

Commit c62f491

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 c072f88 commit c62f491

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
@@ -16,6 +16,11 @@ In each case, the old parameter name remains supported (it cannot be used
1616
simultaneously with the new name), but suppport for it will be dropped in
1717
Matplotlib 3.3.
1818

19+
- The unused ``shape`` and ``imlim`` parameters to `Axes.imshow` are
20+
deprecated. To avoid triggering the deprecation warning, the ``filternorm``,
21+
``filterrad``, ``resample``, and ``url`` arguments should be passed by
22+
keyword.
23+
1924
Deprecations
2025
````````````
2126

lib/matplotlib/axes/_axes.py

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

53965396
#### plotting z(x,y): imshow, pcolor and relatives, contour
53975397
@_preprocess_data()
5398+
@cbook._delete_parameter("3.1", "shape")
5399+
@cbook._delete_parameter("3.1", "imlim")
53985400
def imshow(self, X, cmap=None, norm=None, aspect=None,
53995401
interpolation=None, alpha=None, vmin=None, vmax=None,
54005402
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
_suppress_matplotlib_deprecation_warning,
3737
MatplotlibDeprecationWarning, mplDeprecation)
3838

lib/matplotlib/cbook/deprecation.py

+57
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,63 @@ def wrapper(*args, **kwargs):
310310
return wrapper
311311

312312

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

lib/matplotlib/pyplot.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2644,7 +2644,8 @@ def hlines(
26442644
def imshow(
26452645
X, cmap=None, norm=None, aspect=None, interpolation=None,
26462646
alpha=None, vmin=None, vmax=None, origin=None, extent=None,
2647-
shape=None, filternorm=1, filterrad=4.0, imlim=None,
2647+
shape=cbook.deprecation._deprecated_parameter, filternorm=1,
2648+
filterrad=4.0, imlim=cbook.deprecation._deprecated_parameter,
26482649
resample=None, url=None, *, data=None, **kwargs):
26492650
__ret = gca().imshow(
26502651
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)