From 261044a77168add13a18d982faabea39deb8f6ec Mon Sep 17 00:00:00 2001 From: eric Date: Sun, 18 Feb 2018 13:24:16 -0500 Subject: [PATCH 1/8] [Utils][Warnings] Add deprecation warning for factorize() keyword, order. Add deprecation warning for entire keywords to deprecate_kwaargs util. --- pandas/core/algorithms.py | 2 ++ pandas/tests/util/test_util.py | 14 ++++++++++++++ pandas/util/_decorators.py | 15 +++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index c754c063fce8e..624045a3d64bc 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -32,6 +32,7 @@ from pandas.core import common as com from pandas._libs import algos, lib, hashtable as htable from pandas._libs.tslib import iNaT +from pandas.util._decorators import deprecate_kwarg # --------------- # @@ -436,6 +437,7 @@ def isin(comps, values): return f(comps, values) +@deprecate_kwarg(old_arg_name='order', new_arg_name=None) def factorize(values, sort=False, order=None, na_sentinel=-1, size_hint=None): """ Encode input values as an enumerated type or categorical variable diff --git a/pandas/tests/util/test_util.py b/pandas/tests/util/test_util.py index 3b0a428218771..2bc017ef226ce 100644 --- a/pandas/tests/util/test_util.py +++ b/pandas/tests/util/test_util.py @@ -34,9 +34,14 @@ def _f2(new=False): def _f3(new=0): return new + @deprecate_kwarg('old', None) + def _f4(old=True, unchanged=True): + return old + self.f1 = _f1 self.f2 = _f2 self.f3 = _f3 + self.f4 = _f4 def test_deprecate_kwarg(self): x = 78 @@ -72,6 +77,15 @@ def test_bad_deprecate_kwarg(self): def f4(new=None): pass + def test_deprecate_keyword(self): + x = 9 + with tm.assert_produces_warning(FutureWarning): + result = self.f4(old=x) + assert result is x + with tm.assert_produces_warning(None): + result = self.f4(unchanged=x) + assert result is True + def test_rands(): r = tm.rands(10) diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index eed9cee54efb3..13dd3856f9a37 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -65,8 +65,9 @@ def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2): ---------- old_arg_name : str Name of argument in function to deprecate - new_arg_name : str - Name of preferred argument in function + new_arg_name : str | None + Name of preferred argument in function. Use none to raise warning that + ``old_arg_name`` keyword is deprecated. 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; @@ -107,6 +108,16 @@ def _deprecate_kwarg(func): @wraps(func) def wrapper(*args, **kwargs): old_arg_value = kwargs.pop(old_arg_name, None) + + if new_arg_name is None and old_arg_value is not None: + msg = ( + "the '{old_name}' keyword is no longer supported" + "please takes steps to stop use of '{old_name}'" + ).format(old_name=old_arg_name) + warnings.warn(msg, FutureWarning, stacklevel=stacklevel) + kwargs[old_arg_name] = old_arg_value + return func(*args, **kwargs) + if old_arg_value is not None: if mapping is not None: if hasattr(mapping, 'get'): From ab47916b0ab54203e4ba5677bdc4da815f5b4280 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 19 Feb 2018 19:06:01 -0500 Subject: [PATCH 2/8] PR review for deprecating keyword, order, from factorize(). - Change none to None in docs. - Change warning message to be consistent with other phrasing. - Add tests to catch FutureWarning when order keyword is passed to factorize. --- pandas/tests/test_algos.py | 7 +++++++ pandas/util/_decorators.py | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index b1e3177547ac6..75d61768f0cc0 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -248,6 +248,13 @@ def test_uint64_factorize(self): tm.assert_numpy_array_equal(labels, exp_labels) tm.assert_numpy_array_equal(uniques, exp_uniques) + def test_deprecate_order(self): + data = np.array([2**63, 1, 2**63], dtype=np.uint64) + with tm.assert_produces_warning(expected_warning=FutureWarning): + algos.factorize(data, order=True) + with tm.assert_produces_warning(False): + algos.factorize(data) + class TestUnique(object): diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index 13dd3856f9a37..4c484e8310679 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -66,7 +66,7 @@ def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2): old_arg_name : str Name of argument in function to deprecate new_arg_name : str | None - Name of preferred argument in function. Use none to raise warning that + Name of preferred argument in function. Use None to raise warning that ``old_arg_name`` keyword is deprecated. mapping : dict or callable If mapping is present, use it to translate old arguments to @@ -111,7 +111,8 @@ def wrapper(*args, **kwargs): if new_arg_name is None and old_arg_value is not None: msg = ( - "the '{old_name}' keyword is no longer supported" + "the '{old_name}' keyword is deprecated and will be" + "removed in a future version" "please takes steps to stop use of '{old_name}'" ).format(old_name=old_arg_name) warnings.warn(msg, FutureWarning, stacklevel=stacklevel) From 616c3578739b4d3a76b85058cf4065797a0d4a65 Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 19 Feb 2018 21:40:01 -0500 Subject: [PATCH 3/8] Add examples of how to deprecate a keyword, and fix spacing. --- pandas/tests/test_algos.py | 2 ++ pandas/util/_decorators.py | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index 75d61768f0cc0..884b1eb7342c6 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -249,6 +249,8 @@ def test_uint64_factorize(self): tm.assert_numpy_array_equal(uniques, exp_uniques) def test_deprecate_order(self): + # gh 19727 - check warning is raised for deprecated keyword, order. + # Test not valid once order keyword is removed. data = np.array([2**63, 1, 2**63], dtype=np.uint64) with tm.assert_produces_warning(expected_warning=FutureWarning): algos.factorize(data, order=True) diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index 4c484e8310679..abe79171accf1 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -97,6 +97,24 @@ def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2): FutureWarning: old='yes' is deprecated, use new=True instead warnings.warn(msg, FutureWarning) yes! + + + To raise a warning that a keyword will be removed entirely in the future + + >>> @deprecate_kwarg(old_arg_name='cols', new_arg_name=None) + ... def f(cols='', another_param=''): + ... print(cols) + ... + >>> f(cols='should raise warning') + FutureWarning: the 'cols' keyword is deprecated and will be removed in a + future version please takes steps to stop use of 'cols' + should raise warning + >>> f(another_param='should not raise warning') + should not raise warning + >>> f(cols='should raise warning', another_param='') + FutureWarning: the 'cols' keyword is deprecated and will be removed in a + future version please takes steps to stop use of 'cols' + should raise warning """ if mapping is not None and not hasattr(mapping, 'get') and \ @@ -111,8 +129,8 @@ def wrapper(*args, **kwargs): if new_arg_name is None and old_arg_value is not None: msg = ( - "the '{old_name}' keyword is deprecated and will be" - "removed in a future version" + "the '{old_name}' keyword is deprecated and will be " + "removed in a future version " "please takes steps to stop use of '{old_name}'" ).format(old_name=old_arg_name) warnings.warn(msg, FutureWarning, stacklevel=stacklevel) From 18eb031a9bcbd3af5792e6e6c099ae347bbd7cb9 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Tue, 20 Feb 2018 06:38:11 -0500 Subject: [PATCH 4/8] doc --- pandas/util/_decorators.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index abe79171accf1..2a4996a512b76 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -83,12 +83,15 @@ def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2): ... >>> f(columns='should work ok') should work ok + >>> f(cols='should raise warning') FutureWarning: cols is deprecated, use columns instead warnings.warn(msg, FutureWarning) 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!') @@ -111,6 +114,7 @@ def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2): should raise warning >>> f(another_param='should not raise warning') should not raise warning + >>> f(cols='should raise warning', another_param='') FutureWarning: the 'cols' keyword is deprecated and will be removed in a future version please takes steps to stop use of 'cols' From 6a34d9e25cadd25163663aac06c191eb29bdee14 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 20 Feb 2018 08:59:24 -0500 Subject: [PATCH 5/8] Add to what's new: deprecation of keyword ability, and warning when order is used with functionalize --- doc/source/whatsnew/v0.23.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 11c49995372f5..269d6f9e37f6b 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -571,6 +571,7 @@ Other API Changes - Set operations (union, difference...) on :class:`IntervalIndex` with incompatible index types will now raise a ``TypeError`` rather than a ``ValueError`` (:issue:`19329`) - :class:`DateOffset` objects render more simply, e.g. "" instead of "" (:issue:`19403`) - :func:`pandas.merge` provides a more informative error message when trying to merge on timezone-aware and timezone-naive columns (:issue:`15800`) +- :func:`util._decorators.deprecate_kwarg` now has the ability to deprecate a keyword entirely. Previously, using ``@deprecate_kwarg`` required that an alternative keyword be provided in favor of the deprecated keyword. .. _whatsnew_0230.deprecations: @@ -624,6 +625,7 @@ Removal of prior version deprecations/changes - The modules `pandas.tools.hashing` and `pandas.util.hashing` have been removed (:issue:`16223`) - The top-level functions ``pd.rolling_*``, ``pd.expanding_*`` and ``pd.ewm*`` have been removed (Deprecated since v0.18). Instead, use the DataFrame/Series methods :attr:`~DataFrame.rolling`, :attr:`~DataFrame.expanding` and :attr:`~DataFrame.ewm` (:issue:`18723`) +- Warnings against the obsolete usage of the order keyword in ``core.algorithms.factorize``. For example, ``core.algorithms.factorize(data, order)``, emits a warning that the order keyword will be deprecated (:issue:`19727`) .. _whatsnew_0230.performance: From 4c83659350328784ecdac963f7bb8b6a5acbf577 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 20 Feb 2018 09:00:59 -0500 Subject: [PATCH 6/8] Change '|' to 'or' in docs. --- pandas/util/_decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index 2a4996a512b76..1753bc8b8fc33 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -65,7 +65,7 @@ def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2): ---------- old_arg_name : str Name of argument in function to deprecate - new_arg_name : str | None + new_arg_name : str or None Name of preferred argument in function. Use None to raise warning that ``old_arg_name`` keyword is deprecated. mapping : dict or callable From b5daae00dfae1c714936d49572deb5e985869e00 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 20 Feb 2018 10:40:43 -0500 Subject: [PATCH 7/8] Remove deprecate_kwarg util changes from whats new. --- doc/source/whatsnew/v0.23.0.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index dea87121c8f69..33426452e799d 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -589,7 +589,6 @@ Other API Changes - :func:`Series.to_csv` now accepts a ``compression`` argument that works in the same way as the ``compression`` argument in :func:`DataFrame.to_csv` (:issue:`18958`) - Set operations (union, difference...) on :class:`IntervalIndex` with incompatible index types will now raise a ``TypeError`` rather than a ``ValueError`` (:issue:`19329`) - :class:`DateOffset` objects render more simply, e.g. ```` instead of ```` (:issue:`19403`) -- :func:`util._decorators.deprecate_kwarg` now has the ability to deprecate a keyword entirely. Previously, using ``@deprecate_kwarg`` required that an alternative keyword be provided in favor of the deprecated keyword. .. _whatsnew_0230.deprecations: From bd9717e6c7814117212d2ddf24fc782779b0cfab Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Wed, 21 Feb 2018 06:36:20 -0500 Subject: [PATCH 8/8] doc --- doc/source/whatsnew/v0.23.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 33426452e799d..d4efbb6c3a3fc 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -610,6 +610,7 @@ Deprecations - The ``broadcast`` parameter of ``.apply()`` is deprecated in favor of ``result_type='broadcast'`` (:issue:`18577`) - The ``reduce`` parameter of ``.apply()`` is deprecated in favor of ``result_type='reduce'`` (:issue:`18577`) +- The ``order`` parameter of :func:`factorize` is deprecated and will be removed in a future release (:issue:`19727`) .. _whatsnew_0230.prior_deprecations: @@ -643,7 +644,6 @@ Removal of prior version deprecations/changes - The modules ``pandas.tools.hashing`` and ``pandas.util.hashing`` have been removed (:issue:`16223`) - The top-level functions ``pd.rolling_*``, ``pd.expanding_*`` and ``pd.ewm*`` have been removed (Deprecated since v0.18). Instead, use the DataFrame/Series methods :attr:`~DataFrame.rolling`, :attr:`~DataFrame.expanding` and :attr:`~DataFrame.ewm` (:issue:`18723`) -- Warnings against the obsolete usage of the order keyword in ``core.algorithms.factorize``. For example, ``core.algorithms.factorize(data, order)``, emits a warning that the order keyword will be deprecated (:issue:`19727`) .. _whatsnew_0230.performance: