Skip to content

Commit e52efb6

Browse files
committed
Merge pull request #7991 from ischwabacher/patch-2
Allow deprecate_kwarg to transform arguments
2 parents 0b5fa07 + 18081a1 commit e52efb6

File tree

2 files changed

+97
-5
lines changed

2 files changed

+97
-5
lines changed

pandas/tests/test_util.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
import warnings
3+
4+
import nose
5+
6+
import pandas.util
7+
from pandas.util.decorators import deprecate_kwarg
8+
import pandas.util.testing as tm
9+
10+
class TestDecorators(tm.TestCase):
11+
def setUp(self):
12+
@deprecate_kwarg('old', 'new')
13+
def _f1(new=False):
14+
return new
15+
16+
@deprecate_kwarg('old', 'new', {'yes': True, 'no': False})
17+
def _f2(new=False):
18+
return new
19+
20+
@deprecate_kwarg('old', 'new', lambda x: x+1)
21+
def _f3(new=0):
22+
return new
23+
24+
self.f1 = _f1
25+
self.f2 = _f2
26+
self.f3 = _f3
27+
28+
def test_deprecate_kwarg(self):
29+
x = 78
30+
with tm.assert_produces_warning(FutureWarning):
31+
result = self.f1(old=x)
32+
self.assertIs(result, x)
33+
with tm.assert_produces_warning(None):
34+
self.f1(new=x)
35+
36+
def test_dict_deprecate_kwarg(self):
37+
x = 'yes'
38+
with tm.assert_produces_warning(FutureWarning):
39+
result = self.f2(old=x)
40+
self.assertEqual(result, True)
41+
42+
def test_missing_deprecate_kwarg(self):
43+
x = 'bogus'
44+
with tm.assert_produces_warning(FutureWarning):
45+
result = self.f2(old=x)
46+
self.assertEqual(result, 'bogus')
47+
48+
def test_callable_deprecate_kwarg(self):
49+
x = 5
50+
with tm.assert_produces_warning(FutureWarning):
51+
result = self.f3(old=x)
52+
self.assertEqual(result, x+1)
53+
with tm.assertRaises(TypeError):
54+
self.f3(old='hello')
55+
56+
def test_bad_deprecate_kwarg(self):
57+
with tm.assertRaises(TypeError):
58+
@deprecate_kwarg('old', 'new', 0)
59+
def f4(new=None):
60+
pass
61+
62+
if __name__ == '__main__':
63+
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
64+
exit=False)

pandas/util/decorators.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def wrapper(*args, **kwargs):
1515
return wrapper
1616

1717

18-
def deprecate_kwarg(old_arg_name, new_arg_name):
18+
def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None):
1919
"""Decorator to deprecate a keyword argument of a function
2020
2121
Parameters
@@ -24,14 +24,18 @@ def deprecate_kwarg(old_arg_name, new_arg_name):
2424
Name of argument in function to deprecate
2525
new_arg_name : str
2626
Name of prefered argument in function
27+
mapping : dict or callable
28+
If mapping is present, use it to translate old arguments to
29+
new arguments. A callable must do its own value checking;
30+
values not found in a dict will be forwarded unchanged.
2731
2832
Examples
2933
--------
3034
The following deprecates 'cols', using 'columns' instead
3135
3236
>>> @deprecate_kwarg(old_arg_name='cols', new_arg_name='columns')
3337
... def f(columns=''):
34-
... print columns
38+
... print(columns)
3539
...
3640
>>> f(columns='should work ok')
3741
should work ok
@@ -41,22 +45,46 @@ def deprecate_kwarg(old_arg_name, new_arg_name):
4145
should raise warning
4246
>>> f(cols='should error', columns="can't pass do both")
4347
TypeError: Can only specify 'cols' or 'columns', not both
48+
>>> @deprecate_kwarg('old', 'new', {'yes': True, 'no', False})
49+
... def f(new=False):
50+
... print('yes!' if new else 'no!')
51+
...
52+
>>> f(old='yes')
53+
FutureWarning: old='yes' is deprecated, use new=True instead
54+
warnings.warn(msg, FutureWarning)
55+
yes!
4456
4557
"""
58+
if mapping is not None and not hasattr(mapping, 'get') and \
59+
not callable(mapping):
60+
raise TypeError("mapping from old to new argument values "
61+
"must be dict or callable!")
4662
def _deprecate_kwarg(func):
4763
@wraps(func)
4864
def wrapper(*args, **kwargs):
4965
old_arg_value = kwargs.pop(old_arg_name, None)
5066
if old_arg_value is not None:
51-
msg = "the '%s' keyword is deprecated, use '%s' instead" % \
52-
(old_arg_name, new_arg_name)
67+
if mapping is not None:
68+
if hasattr(mapping, 'get'):
69+
new_arg_value = mapping.get(old_arg_value,
70+
old_arg_value)
71+
else:
72+
new_arg_value = mapping(old_arg_value)
73+
msg = "the %s=%r keyword is deprecated, " \
74+
"use %s=%r instead" % \
75+
(old_arg_name, old_arg_value,
76+
new_arg_name, new_arg_value)
77+
else:
78+
new_arg_value = old_arg_value
79+
msg = "the '%s' keyword is deprecated, " \
80+
"use '%s' instead" % (old_arg_name, new_arg_name)
5381
warnings.warn(msg, FutureWarning)
5482
if kwargs.get(new_arg_name, None) is not None:
5583
msg = "Can only specify '%s' or '%s', not both" % \
5684
(old_arg_name, new_arg_name)
5785
raise TypeError(msg)
5886
else:
59-
kwargs[new_arg_name] = old_arg_value
87+
kwargs[new_arg_name] = new_arg_value
6088
return func(*args, **kwargs)
6189
return wrapper
6290
return _deprecate_kwarg

0 commit comments

Comments
 (0)