Skip to content

Decorator for deleting a parameter with a deprecation period. #13173

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 1 commit into from
Feb 7, 2019
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
5 changes: 5 additions & 0 deletions doc/api/next_api_changes/2018-01-10-AL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ In each case, the old parameter name remains supported (it cannot be used
simultaneously with the new name), but suppport for it will be dropped in
Matplotlib 3.3.

- The unused ``shape`` and ``imlim`` parameters to `Axes.imshow` are
deprecated. To avoid triggering the deprecation warning, the ``filternorm``,
``filterrad``, ``resample``, and ``url`` arguments should be passed by
keyword.

Deprecations
````````````

Expand Down
5 changes: 2 additions & 3 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5395,6 +5395,8 @@ def get_interp_point(ind):

#### plotting z(x,y): imshow, pcolor and relatives, contour
@_preprocess_data()
@cbook._delete_parameter("3.1", "shape")
@cbook._delete_parameter("3.1", "imlim")
def imshow(self, X, cmap=None, norm=None, aspect=None,
interpolation=None, alpha=None, vmin=None, vmax=None,
origin=None, extent=None, shape=None, filternorm=1,
Expand Down Expand Up @@ -5508,9 +5510,6 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
See the example :doc:`/tutorials/intermediate/imshow_extent` for a
more detailed description.

shape : scalars (columns, rows), optional, default: None
For raw buffer images.

filternorm : bool, optional, default: True
A parameter for the antigrain image resize filter (see the
antigrain documentation). If *filternorm* is set, the filter
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

import matplotlib
from .deprecation import (
deprecated, warn_deprecated, _rename_parameter,
deprecated, warn_deprecated, _rename_parameter, _delete_parameter,
_suppress_matplotlib_deprecation_warning,
MatplotlibDeprecationWarning, mplDeprecation)

Expand Down
57 changes: 57 additions & 0 deletions lib/matplotlib/cbook/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,63 @@ def wrapper(*args, **kwargs):
return wrapper


class _deprecated_parameter_class:
def __repr__(self):
return "<deprecated parameter>"


_deprecated_parameter = _deprecated_parameter_class()


def _delete_parameter(since, name, func=None):
"""
Decorator indicating that parameter *name* of *func* is being deprecated.

The actual implementation of *func* should keep the *name* parameter in its
signature.

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.

Examples
--------

::
@_delete_parameter("3.1", "unused")
def func(used_arg, other_arg, unused, more_args): ...
"""

if func is None:
return functools.partial(_delete_parameter, since, name)

signature = inspect.signature(func)
assert name in signature.parameters, (
f"Matplotlib internal error: {name!r} must be a parameter for "
f"{func.__name__}()")
func.__signature__ = signature.replace(parameters=[
param.replace(default=_deprecated_parameter) if param.name == name
else param
for param in signature.parameters.values()])

@functools.wraps(func)
def wrapper(*args, **kwargs):
arguments = func.__signature__.bind(*args, **kwargs).arguments
# We cannot just check `name not in arguments` because the pyplot
# wrappers always pass all arguments explicitly.
if name in arguments and arguments[name] != _deprecated_parameter:
warn_deprecated(
since, message=f"The {name!r} parameter of {func.__name__}() "
f"is deprecated since Matplotlib {since} and will be removed "
f"%(removal)s. If any parameter follows {name!r}, they "
f"should be pass as keyword, not positionally.")
return func(*args, **kwargs)

return wrapper


@contextlib.contextmanager
def _suppress_matplotlib_deprecation_warning():
with warnings.catch_warnings():
Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2644,7 +2644,8 @@ def hlines(
def imshow(
X, cmap=None, norm=None, aspect=None, interpolation=None,
alpha=None, vmin=None, vmax=None, origin=None, extent=None,
shape=None, filternorm=1, filterrad=4.0, imlim=None,
shape=cbook.deprecation._deprecated_parameter, filternorm=1,
filterrad=4.0, imlim=cbook.deprecation._deprecated_parameter,
resample=None, url=None, *, data=None, **kwargs):
__ret = gca().imshow(
X, cmap=cmap, norm=norm, aspect=aspect,
Expand Down
15 changes: 15 additions & 0 deletions lib/matplotlib/tests/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from matplotlib import (
colors, image as mimage, patches, pyplot as plt,
rc_context, rcParams)
from matplotlib.cbook import MatplotlibDeprecationWarning
from matplotlib.image import (AxesImage, BboxImage, FigureImage,
NonUniformImage, PcolorImage)
from matplotlib.testing.decorators import image_comparison
Expand Down Expand Up @@ -944,3 +945,17 @@ def test_relim():
ax.relim()
ax.autoscale()
assert ax.get_xlim() == ax.get_ylim() == (0, 1)


def test_deprecation():
data = [[1, 2], [3, 4]]
ax = plt.figure().subplots()
for obj in [ax, plt]:
with pytest.warns(None) as record:
obj.imshow(data)
assert len(record) == 0
with pytest.warns(MatplotlibDeprecationWarning):
obj.imshow(data, shape=None)
with pytest.warns(MatplotlibDeprecationWarning):
# Enough arguments to pass "shape" positionally.
obj.imshow(data, *[None] * 10)
4 changes: 3 additions & 1 deletion tools/boilerplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

# This line imports the installed copy of matplotlib, and not the local copy.
import numpy as np
from matplotlib import mlab
from matplotlib import cbook, mlab
from matplotlib.axes import Axes


Expand Down Expand Up @@ -175,6 +175,8 @@ def __init__(self, value):
self._repr = "mlab.window_hanning"
elif value is np.mean:
self._repr = "np.mean"
elif value is cbook.deprecation._deprecated_parameter:
self._repr = "cbook.deprecation._deprecated_parameter"
else:
self._repr = repr(value)

Expand Down