Skip to content

Commit e2de657

Browse files
MarcoGorelliJulianWgs
authored andcommitted
Allow deprecate_nonkeyword_arguments to not specify version, show class name (pandas-dev#41524)
1 parent 8fb1565 commit e2de657

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

pandas/io/excel/_base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@
329329
)
330330

331331

332-
@deprecate_nonkeyword_arguments(allowed_args=2, version="2.0")
332+
@deprecate_nonkeyword_arguments(allowed_args=["io", "sheet_name"], version="2.0")
333333
@Appender(_read_excel_doc)
334334
def read_excel(
335335
io,

pandas/tests/util/test_deprecate_nonkeyword_arguments.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_three_positional_argument_with_warning_message_analysis():
6868
for actual_warning in w:
6969
assert actual_warning.category == FutureWarning
7070
assert str(actual_warning.message) == (
71-
"Starting with Pandas version 1.1 all arguments of g "
71+
"Starting with pandas version 1.1 all arguments of g "
7272
"except for the argument 'a' will be keyword-only"
7373
)
7474

@@ -96,6 +96,21 @@ def test_one_positional_argument_with_warning_message_analysis():
9696
for actual_warning in w:
9797
assert actual_warning.category == FutureWarning
9898
assert str(actual_warning.message) == (
99-
"Starting with Pandas version 1.1 all arguments "
99+
"Starting with pandas version 1.1 all arguments "
100100
"of h will be keyword-only"
101101
)
102+
103+
104+
class Foo:
105+
@deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "bar"])
106+
def baz(self, bar=None, foobar=None):
107+
...
108+
109+
110+
def test_class():
111+
msg = (
112+
r"In a future version of pandas all arguments of Foo\.baz "
113+
r"except for the argument \'bar\' will be keyword-only"
114+
)
115+
with tm.assert_produces_warning(FutureWarning, match=msg):
116+
Foo().baz("qux", "quox")

pandas/util/_decorators.py

+27-23
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ def wrapper(*args, **kwargs) -> Callable[..., Any]:
211211
return _deprecate_kwarg
212212

213213

214-
def _format_argument_list(allow_args: list[str] | int):
214+
def _format_argument_list(allow_args: list[str]):
215215
"""
216216
Convert the allow_args argument (either string or integer) of
217217
`deprecate_nonkeyword_arguments` function to a string describing
@@ -231,21 +231,16 @@ def _format_argument_list(allow_args: list[str] | int):
231231
232232
Examples
233233
--------
234-
`format_argument_list(0)` -> ''
235-
`format_argument_list(1)` -> 'except for the first argument'
236-
`format_argument_list(2)` -> 'except for the first 2 arguments'
237234
`format_argument_list([])` -> ''
238235
`format_argument_list(['a'])` -> "except for the arguments 'a'"
239236
`format_argument_list(['a', 'b'])` -> "except for the arguments 'a' and 'b'"
240237
`format_argument_list(['a', 'b', 'c'])` ->
241238
"except for the arguments 'a', 'b' and 'c'"
242239
"""
240+
if "self" in allow_args:
241+
allow_args.remove("self")
243242
if not allow_args:
244243
return ""
245-
elif allow_args == 1:
246-
return " except for the first argument"
247-
elif isinstance(allow_args, int):
248-
return f" except for the first {allow_args} arguments"
249244
elif len(allow_args) == 1:
250245
return f" except for the argument '{allow_args[0]}'"
251246
else:
@@ -254,9 +249,17 @@ def _format_argument_list(allow_args: list[str] | int):
254249
return f" except for the arguments {args} and '{last}'"
255250

256251

252+
def future_version_msg(version: str | None) -> str:
253+
"""Specify which version of pandas the deprecation will take place in."""
254+
if version is None:
255+
return "In a future version of pandas"
256+
else:
257+
return f"Starting with pandas version {version}"
258+
259+
257260
def deprecate_nonkeyword_arguments(
258-
version: str,
259-
allowed_args: list[str] | int | None = None,
261+
version: str | None,
262+
allowed_args: list[str] | None = None,
260263
stacklevel: int = 2,
261264
) -> Callable:
262265
"""
@@ -266,14 +269,13 @@ def deprecate_nonkeyword_arguments(
266269
----------
267270
version : str
268271
The version in which positional arguments will become
269-
keyword-only.
272+
keyword-only. If None, then the warning message won't
273+
specify any particular version.
270274
271-
allowed_args : list or int, optional
275+
allowed_args : list, optional
272276
In case of list, it must be the list of names of some
273277
first arguments of the decorated functions that are
274-
OK to be given as positional arguments. In case of an
275-
integer, this is the number of positional arguments
276-
that will stay positional. In case of None value,
278+
OK to be given as positional arguments. In case of None value,
277279
defaults to list of all arguments not having the
278280
default value.
279281
@@ -291,19 +293,21 @@ def decorate(func):
291293
assert spec.defaults is not None # for mypy
292294
allow_args = spec.args[: -len(spec.defaults)]
293295

296+
num_allow_args = len(allow_args)
297+
msg = (
298+
f"{future_version_msg(version)} all arguments of "
299+
f"{func.__qualname__}{{arguments}} will be keyword-only"
300+
)
301+
294302
@wraps(func)
295303
def wrapper(*args, **kwargs):
296304
arguments = _format_argument_list(allow_args)
297-
if isinstance(allow_args, (list, tuple)):
298-
num_allow_args = len(allow_args)
299-
else:
300-
num_allow_args = allow_args
301305
if len(args) > num_allow_args:
302-
msg = (
303-
f"Starting with Pandas version {version} all arguments of "
304-
f"{func.__name__}{arguments} will be keyword-only"
306+
warnings.warn(
307+
msg.format(arguments=arguments),
308+
FutureWarning,
309+
stacklevel=stacklevel,
305310
)
306-
warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
307311
return func(*args, **kwargs)
308312

309313
return wrapper

0 commit comments

Comments
 (0)