Skip to content

Commit f450173

Browse files
thomasjpfanrth
authored andcommitted
ENH Add Deprecating Position Arguments Helper (scikit-learn#13311)
1 parent 16f4ac9 commit f450173

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

sklearn/utils/tests/test_validation.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
check_non_negative,
4242
_num_samples,
4343
check_scalar,
44+
_deprecate_positional_args,
4445
_check_sample_weight,
4546
_allclose_dense_sparse)
4647
import sklearn
@@ -958,3 +959,55 @@ def test_allclose_dense_sparse_raise(toarray):
958959
"and an array")
959960
with pytest.raises(ValueError, match=msg):
960961
_allclose_dense_sparse(x, y)
962+
963+
964+
def test_deprecate_positional_args_warns_for_function():
965+
966+
@_deprecate_positional_args
967+
def f1(a, b, *, c=1, d=1):
968+
pass
969+
970+
with pytest.warns(DeprecationWarning,
971+
match=r"Pass c=3 as keyword args"):
972+
f1(1, 2, 3)
973+
974+
with pytest.warns(DeprecationWarning,
975+
match=r"Pass c=3, d=4 as keyword args"):
976+
f1(1, 2, 3, 4)
977+
978+
@_deprecate_positional_args
979+
def f2(a=1, *, b=1, c=1, d=1):
980+
pass
981+
982+
with pytest.warns(DeprecationWarning,
983+
match=r"Pass b=2 as keyword args"):
984+
f2(1, 2)
985+
986+
987+
def test_deprecate_positional_args_warns_for_class():
988+
989+
class A1:
990+
@_deprecate_positional_args
991+
def __init__(self, a, b, *, c=1, d=1):
992+
pass
993+
994+
with pytest.warns(DeprecationWarning,
995+
match=r"Pass c=3 as keyword args"):
996+
A1(1, 2, 3)
997+
998+
with pytest.warns(DeprecationWarning,
999+
match=r"Pass c=3, d=4 as keyword args"):
1000+
A1(1, 2, 3, 4)
1001+
1002+
class A2:
1003+
@_deprecate_positional_args
1004+
def __init__(self, a=1, b=1, *, c=1, d=1):
1005+
pass
1006+
1007+
with pytest.warns(DeprecationWarning,
1008+
match=r"Pass c=3 as keyword args"):
1009+
A2(1, 2, 3)
1010+
1011+
with pytest.warns(DeprecationWarning,
1012+
match=r"Pass c=3, d=4 as keyword args"):
1013+
A2(1, 2, 3, 4)

sklearn/utils/validation.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
# Nicolas Tresegnie
99
# License: BSD 3 clause
1010

11+
from functools import wraps
1112
import warnings
1213
import numbers
1314

1415
import numpy as np
1516
import scipy.sparse as sp
1617
from distutils.version import LooseVersion
17-
from inspect import signature, isclass
18+
from inspect import signature, isclass, Parameter
1819

1920
from numpy.core.numeric import ComplexWarning
2021
import joblib
@@ -1104,3 +1105,41 @@ def _allclose_dense_sparse(x, y, rtol=1e-7, atol=1e-9):
11041105
return np.allclose(x, y, rtol=rtol, atol=atol)
11051106
raise ValueError("Can only compare two sparse matrices, not a sparse "
11061107
"matrix and an array")
1108+
1109+
1110+
def _deprecate_positional_args(f):
1111+
"""Decorator for methods that issues warnings for positional arguments
1112+
1113+
Using the keyword-only argument syntax in pep 3102, arguments after the
1114+
* will issue a warning when passed as a positional argument.
1115+
1116+
Parameters
1117+
----------
1118+
f : function
1119+
function to check arguments on
1120+
"""
1121+
sig = signature(f)
1122+
kwonly_args = []
1123+
all_args = []
1124+
1125+
for name, param in sig.parameters.items():
1126+
if param.kind == Parameter.POSITIONAL_OR_KEYWORD:
1127+
all_args.append(name)
1128+
elif param.kind == Parameter.KEYWORD_ONLY:
1129+
kwonly_args.append(name)
1130+
1131+
@wraps(f)
1132+
def inner_f(*args, **kwargs):
1133+
extra_args = len(args) - len(all_args)
1134+
if extra_args > 0:
1135+
# ignore first 'self' argument for instance methods
1136+
args_msg = ['{}={}'.format(name, arg)
1137+
for name, arg in zip(kwonly_args[:extra_args],
1138+
args[-extra_args:])]
1139+
warnings.warn("Pass {} as keyword args. From version 0.24 "
1140+
"passing these as positional arguments will "
1141+
"result in an error".format(", ".join(args_msg)),
1142+
DeprecationWarning)
1143+
kwargs.update({k: arg for k, arg in zip(all_args, args)})
1144+
return f(**kwargs)
1145+
return inner_f

0 commit comments

Comments
 (0)