Skip to content

Add a make-parameter-keyword-only-with-deprecation decorator. #13601

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
Mar 13, 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
9 changes: 9 additions & 0 deletions doc/api/next_api_changes/2019-03-07-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
API changes
```````````

Passing the ``block`` argument of ``plt.show`` positionally is deprecated; it
should be passed by keyword.

When using the nbagg backend, ``plt.show`` used to silently accept and ignore
all combinations of positional and keyword arguments. This behavior is
deprecated.
4 changes: 3 additions & 1 deletion lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3216,8 +3216,10 @@ def draw_if_interactive(cls):
cls.trigger_manager_draw(manager)

@classmethod
@cbook._make_keyword_only("3.1", "block")
def show(cls, block=None):
"""Show all figures.
"""
Show all figures.

`show` blocks by calling `mainloop` if *block* is ``True``, or if it
is ``None`` and we are neither in IPython's ``%pylab`` mode, nor in
Expand Down
10 changes: 8 additions & 2 deletions lib/matplotlib/backends/backend_nbagg.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# Jupyter/IPython 3.x or earlier
from IPython.kernel.comm import Comm

from matplotlib import is_interactive
from matplotlib import cbook, is_interactive
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import (
_Backend, FigureCanvasBase, NavigationToolbar2)
Expand Down Expand Up @@ -241,7 +241,13 @@ def trigger_manager_draw(manager):
manager.show()

@staticmethod
def show(*args, **kwargs):
def show(*args, block=None, **kwargs):
if args or kwargs:
cbook.warn_deprecated(
"3.1", message="Passing arguments to show(), other than "
"passing 'block' by keyword, is deprecated %(since)s, and "
"support for it will be removed %(removal)s.")

## TODO: something to do when keyword block==False ?
from matplotlib._pylab_helpers import Gcf

Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/backends/backend_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def draw_if_interactive():
"""


def show(block=None):
def show(*, block=None):
"""
For image backends - is not required.
For GUI backends - show() is usually the last line of a pyplot script and
Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@

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

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


def _make_keyword_only(since, name, func=None):
"""
Decorator indicating that passing parameter *name* (or any of the following
ones) positionally to *func* is being deprecated.

Note that this decorator **cannot** be applied to a function that has a
pyplot-level wrapper, as the wrapper always pass all arguments by keyword.
If it is used, users will see spurious DeprecationWarnings every time they
call the pyplot wrapper.
"""

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

signature = inspect.signature(func)
POK = inspect.Parameter.POSITIONAL_OR_KEYWORD
KWO = inspect.Parameter.KEYWORD_ONLY
assert (name in signature.parameters
and signature.parameters[name].kind == POK), (
f"Matplotlib internal error: {name!r} must be a positional-or-keyword "
f"parameter for {func.__name__}()")
names = [*signature.parameters]
kwonly = [name for name in names[names.index(name):]
if signature.parameters[name].kind == POK]
func.__signature__ = signature.replace(parameters=[
param.replace(kind=inspect.Parameter.KEYWORD_ONLY)
if param.name in kwonly
else param
for param in signature.parameters.values()])

@functools.wraps(func)
def wrapper(*args, **kwargs):
bound = signature.bind(*args, **kwargs)
if name in bound.arguments and name not in kwargs:
warn_deprecated(
since, message="Passing the %(name)s %(obj_type)s "
"positionally is deprecated since Matplotlib %(since)s; the "
"parameter will become keyword-only %(removal)s.",
name=name, obj_type=f"parameter of {func.__name__}()")
return func(*args, **kwargs)

return wrapper


@contextlib.contextmanager
def _suppress_matplotlib_deprecation_warning():
with warnings.catch_warnings():
Expand Down
17 changes: 16 additions & 1 deletion lib/matplotlib/tests/test_cbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

import matplotlib.cbook as cbook
import matplotlib.colors as mcolors
from matplotlib.cbook import delete_masked_points as dmp
from matplotlib.cbook import (
MatplotlibDeprecationWarning, delete_masked_points as dmp)


def test_is_hashable():
Expand Down Expand Up @@ -545,3 +546,17 @@ def test_safe_first_element_pandas_series(pd):
s = pd.Series(range(5), index=range(10, 15))
actual = cbook.safe_first_element(s)
assert actual == 0


def test_make_keyword_only(recwarn):
@cbook._make_keyword_only("3.0", "arg")
def func(pre, arg, post=None):
pass

func(1, arg=2)
assert len(recwarn) == 0

with pytest.warns(MatplotlibDeprecationWarning):
func(1, 2)
with pytest.warns(MatplotlibDeprecationWarning):
func(1, 2, 3)