Skip to content

Allow deprecate_nonkeyword_arguments to not specify version, show class name #41524

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
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion pandas/io/excel/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@
)


@deprecate_nonkeyword_arguments(allowed_args=2, version="2.0")
@deprecate_nonkeyword_arguments(allowed_args=["io", "sheet_name"], version="2.0")
@Appender(_read_excel_doc)
def read_excel(
io,
Expand Down
19 changes: 17 additions & 2 deletions pandas/tests/util/test_deprecate_nonkeyword_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_three_positional_argument_with_warning_message_analysis():
for actual_warning in w:
assert actual_warning.category == FutureWarning
assert str(actual_warning.message) == (
"Starting with Pandas version 1.1 all arguments of g "
"Starting with pandas version 1.1 all arguments of g "
"except for the argument 'a' will be keyword-only"
)

Expand Down Expand Up @@ -96,6 +96,21 @@ def test_one_positional_argument_with_warning_message_analysis():
for actual_warning in w:
assert actual_warning.category == FutureWarning
assert str(actual_warning.message) == (
"Starting with Pandas version 1.1 all arguments "
"Starting with pandas version 1.1 all arguments "
"of h will be keyword-only"
)


class Foo:
@deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "bar"])
def baz(self, bar=None, foobar=None):
...


def test_class():
msg = (
r"In a future version of pandas all arguments of Foo\.baz "
r"except for the argument \'bar\' will be keyword-only"
)
with tm.assert_produces_warning(FutureWarning, match=msg):
Foo().baz("qux", "quox")
41 changes: 20 additions & 21 deletions pandas/util/_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def wrapper(*args, **kwargs) -> Callable[..., Any]:
return _deprecate_kwarg


def _format_argument_list(allow_args: list[str] | int):
def _format_argument_list(allow_args: list[str]):
"""
Convert the allow_args argument (either string or integer) of
`deprecate_nonkeyword_arguments` function to a string describing
Expand All @@ -231,21 +231,16 @@ def _format_argument_list(allow_args: list[str] | int):

Examples
--------
`format_argument_list(0)` -> ''
`format_argument_list(1)` -> 'except for the first argument'
`format_argument_list(2)` -> 'except for the first 2 arguments'
`format_argument_list([])` -> ''
`format_argument_list(['a'])` -> "except for the arguments 'a'"
`format_argument_list(['a', 'b'])` -> "except for the arguments 'a' and 'b'"
`format_argument_list(['a', 'b', 'c'])` ->
"except for the arguments 'a', 'b' and 'c'"
"""
if "self" in allow_args:
allow_args.remove("self")
if not allow_args:
return ""
elif allow_args == 1:
return " except for the first argument"
elif isinstance(allow_args, int):
return f" except for the first {allow_args} arguments"
elif len(allow_args) == 1:
return f" except for the argument '{allow_args[0]}'"
else:
Expand All @@ -254,9 +249,17 @@ def _format_argument_list(allow_args: list[str] | int):
return f" except for the arguments {args} and '{last}'"


def future_version_msg(version: str | None) -> str:
"""Specify which version of pandas the deprecation will take place in."""
if version is None:
return "In a future version of pandas"
else:
return f"Starting with pandas version {version}"


def deprecate_nonkeyword_arguments(
version: str,
allowed_args: list[str] | int | None = None,
version: str | None,
allowed_args: list[str] | None = None,
stacklevel: int = 2,
) -> Callable:
"""
Expand All @@ -266,14 +269,13 @@ def deprecate_nonkeyword_arguments(
----------
version : str
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional

The version in which positional arguments will become
keyword-only.
keyword-only. If None, then the warning message won't
specify any particular version.

allowed_args : list or int, optional
allowed_args : list, optional
In case of list, it must be the list of names of some
first arguments of the decorated functions that are
OK to be given as positional arguments. In case of an
integer, this is the number of positional arguments
that will stay positional. In case of None value,
OK to be given as positional arguments. In case of None value,
defaults to list of all arguments not having the
default value.

Expand All @@ -294,14 +296,11 @@ def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
arguments = _format_argument_list(allow_args)
if isinstance(allow_args, (list, tuple)):
num_allow_args = len(allow_args)
else:
num_allow_args = allow_args
num_allow_args = len(allow_args)
Copy link
Member

@simonjayhawkins simonjayhawkins May 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nbd, but this and the line above could be moved to the outer scope.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed - have fixed, thanks!

if len(args) > num_allow_args:
msg = (
f"Starting with Pandas version {version} all arguments of "
f"{func.__name__}{arguments} will be keyword-only"
f"{future_version_msg(version)} all arguments of "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for the message creation

f"{func.__qualname__}{arguments} will be keyword-only"
)
warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
return func(*args, **kwargs)
Expand Down