Skip to content

Allow deprecate_kwarg to transform arguments #7991

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
Sep 9, 2014
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
64 changes: 64 additions & 0 deletions pandas/tests/test_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@

import warnings

import nose

import pandas.util
from pandas.util.decorators import deprecate_kwarg
import pandas.util.testing as tm

class TestDecorators(tm.TestCase):
def setUp(self):
@deprecate_kwarg('old', 'new')
def _f1(new=False):
return new

@deprecate_kwarg('old', 'new', {'yes': True, 'no': False})
def _f2(new=False):
return new

@deprecate_kwarg('old', 'new', lambda x: x+1)
def _f3(new=0):
return new

self.f1 = _f1
self.f2 = _f2
self.f3 = _f3

def test_deprecate_kwarg(self):
x = 78
with tm.assert_produces_warning(FutureWarning):
result = self.f1(old=x)
self.assertIs(result, x)
with tm.assert_produces_warning(None):
self.f1(new=x)

def test_dict_deprecate_kwarg(self):
x = 'yes'
with tm.assert_produces_warning(FutureWarning):
result = self.f2(old=x)
self.assertEqual(result, True)

def test_missing_deprecate_kwarg(self):
x = 'bogus'
with tm.assert_produces_warning(FutureWarning):
result = self.f2(old=x)
self.assertEqual(result, 'bogus')

def test_callable_deprecate_kwarg(self):
x = 5
with tm.assert_produces_warning(FutureWarning):
result = self.f3(old=x)
self.assertEqual(result, x+1)
with tm.assertRaises(TypeError):
self.f3(old='hello')

def test_bad_deprecate_kwarg(self):
with tm.assertRaises(TypeError):
@deprecate_kwarg('old', 'new', 0)
def f4(new=None):
pass

if __name__ == '__main__':
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
exit=False)
38 changes: 33 additions & 5 deletions pandas/util/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def wrapper(*args, **kwargs):
return wrapper


def deprecate_kwarg(old_arg_name, new_arg_name):
def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None):
"""Decorator to deprecate a keyword argument of a function

Parameters
Expand All @@ -24,14 +24,18 @@ def deprecate_kwarg(old_arg_name, new_arg_name):
Name of argument in function to deprecate
new_arg_name : str
Name of prefered argument in function
mapping : dict or callable
If mapping is present, use it to translate old arguments to
new arguments. A callable must do its own value checking;
values not found in a dict will be forwarded unchanged.

Examples
--------
The following deprecates 'cols', using 'columns' instead

>>> @deprecate_kwarg(old_arg_name='cols', new_arg_name='columns')
... def f(columns=''):
... print columns
... print(columns)
...
>>> f(columns='should work ok')
should work ok
Expand All @@ -41,22 +45,46 @@ def deprecate_kwarg(old_arg_name, new_arg_name):
should raise warning
>>> f(cols='should error', columns="can't pass do both")
TypeError: Can only specify 'cols' or 'columns', not both
>>> @deprecate_kwarg('old', 'new', {'yes': True, 'no', False})
... def f(new=False):
... print('yes!' if new else 'no!')
...
>>> f(old='yes')
FutureWarning: old='yes' is deprecated, use new=True instead
warnings.warn(msg, FutureWarning)
yes!

"""
if mapping is not None and not hasattr(mapping, 'get') and \
not callable(mapping):
raise TypeError("mapping from old to new argument values "
"must be dict or callable!")
def _deprecate_kwarg(func):
@wraps(func)
def wrapper(*args, **kwargs):
old_arg_value = kwargs.pop(old_arg_name, None)
if old_arg_value is not None:
msg = "the '%s' keyword is deprecated, use '%s' instead" % \
(old_arg_name, new_arg_name)
if mapping is not None:
if hasattr(mapping, 'get'):
new_arg_value = mapping.get(old_arg_value,
old_arg_value)
else:
new_arg_value = mapping(old_arg_value)
msg = "the %s=%r keyword is deprecated, " \
"use %s=%r instead" % \
(old_arg_name, old_arg_value,
new_arg_name, new_arg_value)
else:
new_arg_value = old_arg_value
msg = "the '%s' keyword is deprecated, " \
"use '%s' instead" % (old_arg_name, new_arg_name)
warnings.warn(msg, FutureWarning)
if kwargs.get(new_arg_name, None) is not None:
msg = "Can only specify '%s' or '%s', not both" % \
(old_arg_name, new_arg_name)
raise TypeError(msg)
else:
kwargs[new_arg_name] = old_arg_value
kwargs[new_arg_name] = new_arg_value
return func(*args, **kwargs)
return wrapper
return _deprecate_kwarg
Expand Down