From efcef7846f8db76ddc8ac667e55909cb5248f07c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 20 Nov 2017 15:34:57 -0800 Subject: [PATCH 1/2] Pin correct names to wrapped Index comparison methods --- pandas/core/indexes/base.py | 1 + pandas/core/indexes/category.py | 1 + pandas/core/indexes/datetimes.py | 12 +++++++----- pandas/core/indexes/period.py | 2 ++ pandas/core/indexes/timedeltas.py | 1 + pandas/tests/indexes/test_base.py | 10 ++++++++++ 6 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 1359a938e652d..b4d82751de50c 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -3890,6 +3890,7 @@ def _evaluate_compare(self, other): except TypeError: return result + _evaluate_compare.__name__ = '__{name}__'.format(name=op.__name__) return _evaluate_compare cls.__eq__ = _make_compare(operator.eq) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 84d4585f0a56c..bee3a86e0c6b6 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -747,6 +747,7 @@ def _evaluate_compare(self, other): return getattr(self.values, op)(other) + _evaluate_compare.__name__ = op return _evaluate_compare cls.__eq__ = _make_compare('__eq__') diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 64b5b9f958880..dfff789071319 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -135,6 +135,7 @@ def wrapper(self, other): return result return Index(result) + wrapper.__name__ = opname return wrapper @@ -1596,14 +1597,15 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): else: raise - # alias to offset - def _get_freq(self): + @property + def freq(self): + """get/set the frequency of the Index""" return self.offset - def _set_freq(self, value): + @freq.setter + def freq(self, value): + """get/set the frequency of the Index""" self.offset = value - freq = property(fget=_get_freq, fset=_set_freq, - doc="get/set the frequency of the Index") year = _field_accessor('year', 'Y', "The year of the datetime") month = _field_accessor('month', 'M', diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 85e3300913000..e1061cda9daf8 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -115,6 +115,8 @@ def wrapper(self, other): result[self._isnan] = nat_result return result + + wrapper.__name__ = opname return wrapper diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index c592aa9608d97..3b93f173a8a2a 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -93,6 +93,7 @@ def wrapper(self, other): return result return Index(result) + wrapper.__name__ = opname return wrapper diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 307cda7f2d1cb..f60a6c7360dde 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -2174,3 +2174,13 @@ class TestIndexUtils(object): def test_ensure_index_from_sequences(self, data, names, expected): result = _ensure_index_from_sequences(data, names) tm.assert_index_equal(result, expected) + + +def test_generated_op_names(indices): + index = indices + assert index.__eq__.__name__ == '__eq__' + assert index.__ne__.__name__ == '__ne__' + assert index.__le__.__name__ == '__le__' + assert index.__lt__.__name__ == '__lt__' + assert index.__ge__.__name__ == '__ge__' + assert index.__gt__.__name__ == '__gt__' From c36e8704447305a1163f064acba4c891379f9a56 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 20 Nov 2017 17:09:54 -0800 Subject: [PATCH 2/2] use set_function_name pattern --- pandas/core/indexes/base.py | 6 +++--- pandas/core/indexes/category.py | 7 +++---- pandas/core/indexes/datetimes.py | 21 ++++++++++++--------- pandas/core/indexes/period.py | 21 ++++++++++++--------- pandas/core/indexes/timedeltas.py | 21 ++++++++++++--------- pandas/tests/indexes/test_base.py | 12 +++++------- 6 files changed, 47 insertions(+), 41 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index b4d82751de50c..944a2aadfeb3b 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -9,7 +9,7 @@ from pandas._libs.lib import is_datetime_array from pandas._libs.tslibs import parsing -from pandas.compat import range, u +from pandas.compat import range, u, set_function_name from pandas.compat.numpy import function as nv from pandas import compat @@ -3890,8 +3890,8 @@ def _evaluate_compare(self, other): except TypeError: return result - _evaluate_compare.__name__ = '__{name}__'.format(name=op.__name__) - return _evaluate_compare + name = '__{name}__'.format(name=op.__name__) + return set_function_name(_evaluate_compare, name, cls) cls.__eq__ = _make_compare(operator.eq) cls.__ne__ = _make_compare(operator.ne) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index bee3a86e0c6b6..d09e5447431ce 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -722,7 +722,7 @@ def _codes_for_groupby(self, sort): def _add_comparison_methods(cls): """ add in comparison methods """ - def _make_compare(op): + def _make_compare(opname): def _evaluate_compare(self, other): # if we have a Categorical type, then must have the same @@ -745,10 +745,9 @@ def _evaluate_compare(self, other): "have the same categories and ordered " "attributes") - return getattr(self.values, op)(other) + return getattr(self.values, opname)(other) - _evaluate_compare.__name__ = op - return _evaluate_compare + return compat.set_function_name(_evaluate_compare, opname, cls) cls.__eq__ = _make_compare('__eq__') cls.__ne__ = _make_compare('__ne__') diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index dfff789071319..4f706c3b29a07 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -98,7 +98,7 @@ def f(self): return property(f) -def _dt_index_cmp(opname, nat_result=False): +def _dt_index_cmp(opname, cls, nat_result=False): """ Wrap comparison operations to convert datetime-like to datetime64 """ @@ -135,8 +135,7 @@ def wrapper(self, other): return result return Index(result) - wrapper.__name__ = opname - return wrapper + return compat.set_function_name(wrapper, opname, cls) def _ensure_datetime64(other): @@ -278,12 +277,15 @@ def _join_i8_wrapper(joinf, **kwargs): libjoin.left_join_indexer_unique_int64, with_indexers=False) _arrmap = None - __eq__ = _dt_index_cmp('__eq__') - __ne__ = _dt_index_cmp('__ne__', nat_result=True) - __lt__ = _dt_index_cmp('__lt__') - __gt__ = _dt_index_cmp('__gt__') - __le__ = _dt_index_cmp('__le__') - __ge__ = _dt_index_cmp('__ge__') + @classmethod + def _add_comparison_methods(cls): + """ add in comparison methods """ + cls.__eq__ = _dt_index_cmp('__eq__', cls) + cls.__ne__ = _dt_index_cmp('__ne__', cls, nat_result=True) + cls.__lt__ = _dt_index_cmp('__lt__', cls) + cls.__gt__ = _dt_index_cmp('__gt__', cls) + cls.__le__ = _dt_index_cmp('__le__', cls) + cls.__ge__ = _dt_index_cmp('__ge__', cls) _engine_type = libindex.DatetimeEngine @@ -2013,6 +2015,7 @@ def to_julian_date(self): ) / 24.0) +DatetimeIndex._add_comparison_methods() DatetimeIndex._add_numeric_methods_disabled() DatetimeIndex._add_logical_methods_disabled() DatetimeIndex._add_datetimelike_methods() diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index e1061cda9daf8..43522257bd047 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -77,7 +77,7 @@ def dt64arr_to_periodarr(data, freq, tz): _DIFFERENT_FREQ_INDEX = period._DIFFERENT_FREQ_INDEX -def _period_index_cmp(opname, nat_result=False): +def _period_index_cmp(opname, cls, nat_result=False): """ Wrap comparison operations to convert datetime-like to datetime64 """ @@ -116,8 +116,7 @@ def wrapper(self, other): return result - wrapper.__name__ = opname - return wrapper + return compat.set_function_name(wrapper, opname, cls) def _new_PeriodIndex(cls, **d): @@ -229,12 +228,15 @@ class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin, Int64Index): _engine_type = libindex.PeriodEngine - __eq__ = _period_index_cmp('__eq__') - __ne__ = _period_index_cmp('__ne__', nat_result=True) - __lt__ = _period_index_cmp('__lt__') - __gt__ = _period_index_cmp('__gt__') - __le__ = _period_index_cmp('__le__') - __ge__ = _period_index_cmp('__ge__') + @classmethod + def _add_comparison_methods(cls): + """ add in comparison methods """ + cls.__eq__ = _period_index_cmp('__eq__', cls) + cls.__ne__ = _period_index_cmp('__ne__', cls, nat_result=True) + cls.__lt__ = _period_index_cmp('__lt__', cls) + cls.__gt__ = _period_index_cmp('__gt__', cls) + cls.__le__ = _period_index_cmp('__le__', cls) + cls.__ge__ = _period_index_cmp('__ge__', cls) def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None, periods=None, copy=False, name=None, tz=None, dtype=None, @@ -1104,6 +1106,7 @@ def tz_localize(self, tz, infer_dst=False): raise NotImplementedError("Not yet implemented for PeriodIndex") +PeriodIndex._add_comparison_methods() PeriodIndex._add_numeric_methods_disabled() PeriodIndex._add_logical_methods_disabled() PeriodIndex._add_datetimelike_methods() diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 3b93f173a8a2a..93329f4b4e65a 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -52,7 +52,7 @@ def f(self): return property(f) -def _td_index_cmp(opname, nat_result=False): +def _td_index_cmp(opname, cls, nat_result=False): """ Wrap comparison operations to convert timedelta-like to timedelta64 """ @@ -93,8 +93,7 @@ def wrapper(self, other): return result return Index(result) - wrapper.__name__ = opname - return wrapper + return compat.set_function_name(wrapper, opname, cls) class TimedeltaIndex(DatetimeIndexOpsMixin, TimelikeOps, Int64Index): @@ -181,12 +180,15 @@ def _join_i8_wrapper(joinf, **kwargs): _datetimelike_methods = ["to_pytimedelta", "total_seconds", "round", "floor", "ceil"] - __eq__ = _td_index_cmp('__eq__') - __ne__ = _td_index_cmp('__ne__', nat_result=True) - __lt__ = _td_index_cmp('__lt__') - __gt__ = _td_index_cmp('__gt__') - __le__ = _td_index_cmp('__le__') - __ge__ = _td_index_cmp('__ge__') + @classmethod + def _add_comparison_methods(cls): + """ add in comparison methods """ + cls.__eq__ = _td_index_cmp('__eq__', cls) + cls.__ne__ = _td_index_cmp('__ne__', cls, nat_result=True) + cls.__lt__ = _td_index_cmp('__lt__', cls) + cls.__gt__ = _td_index_cmp('__gt__', cls) + cls.__le__ = _td_index_cmp('__le__', cls) + cls.__ge__ = _td_index_cmp('__ge__', cls) _engine_type = libindex.TimedeltaEngine @@ -913,6 +915,7 @@ def delete(self, loc): return TimedeltaIndex(new_tds, name=self.name, freq=freq) +TimedeltaIndex._add_comparison_methods() TimedeltaIndex._add_numeric_methods() TimedeltaIndex._add_logical_methods_disabled() TimedeltaIndex._add_datetimelike_methods() diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index f60a6c7360dde..c55f53601848c 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -2176,11 +2176,9 @@ def test_ensure_index_from_sequences(self, data, names, expected): tm.assert_index_equal(result, expected) -def test_generated_op_names(indices): +@pytest.mark.parametrize('opname', ['eq', 'ne', 'le', 'lt', 'ge', 'gt']) +def test_generated_op_names(opname, indices): index = indices - assert index.__eq__.__name__ == '__eq__' - assert index.__ne__.__name__ == '__ne__' - assert index.__le__.__name__ == '__le__' - assert index.__lt__.__name__ == '__lt__' - assert index.__ge__.__name__ == '__ge__' - assert index.__gt__.__name__ == '__gt__' + opname = '__{name}__'.format(name=opname) + method = getattr(index, opname) + assert method.__name__ == opname