From 0b489a5fb2b538c1da23e700fda997cd41baf5bd Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 24 Feb 2020 18:35:29 -0800 Subject: [PATCH 1/3] pin down kwargs in _shallow_copy --- pandas/core/indexes/base.py | 12 +++++------- pandas/core/indexes/category.py | 6 ++++-- pandas/core/indexes/period.py | 18 +++++------------- pandas/core/indexes/range.py | 9 +++++---- pandas/tests/indexes/period/test_period.py | 8 +------- 5 files changed, 20 insertions(+), 33 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index aa22527d8c2d7..42966d3a335e0 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -8,7 +8,7 @@ from pandas._libs import algos as libalgos, index as libindex, lib import pandas._libs.join as libjoin -from pandas._libs.lib import is_datetime_array +from pandas._libs.lib import is_datetime_array, no_default from pandas._libs.tslibs import OutOfBoundsDatetime, Timestamp from pandas._libs.tslibs.period import IncompatibleFrequency from pandas._libs.tslibs.timezones import tz_compare @@ -485,7 +485,7 @@ def _get_attributes_dict(self): """ return {k: getattr(self, k, None) for k in self._attributes} - def _shallow_copy(self, values=None, **kwargs): + def _shallow_copy(self, values=None, name=no_default): """ Create a new Index with the same class as the caller, don't copy the data, use the same object attributes with passed in attributes taking @@ -498,14 +498,12 @@ def _shallow_copy(self, values=None, **kwargs): values : the values to create the new Index, optional kwargs : updates the default attributes for this Index """ + name = self.name if name is no_default else name + if values is None: values = self.values - attributes = self._get_attributes_dict() - - attributes.update(kwargs) - - return self._simple_new(values, **attributes) + return self._simple_new(values, name=name) def _shallow_copy_with_infer(self, values, **kwargs): """ diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index caa6a9a93141f..0bd3b86b25579 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -7,6 +7,7 @@ from pandas._libs import index as libindex from pandas._libs.hashtable import duplicated_int64 +from pandas._libs.lib import no_default from pandas.util._decorators import Appender, cache_readonly from pandas.core.dtypes.common import ( @@ -264,13 +265,14 @@ def _simple_new(cls, values, name=None, dtype=None): # -------------------------------------------------------------------- @Appender(Index._shallow_copy.__doc__) - def _shallow_copy(self, values=None, **kwargs): + def _shallow_copy(self, values=None, name=no_default): + name = self.name if name is no_default else name + if values is None: values = self.values cat = Categorical(values, dtype=self.dtype) - name = kwargs.get("name", self.name) return type(self)._simple_new(cat, name=name) def _is_dtype_compat(self, other) -> bool: diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 0b85433b699a8..2c159e5b4199b 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -5,6 +5,7 @@ import numpy as np from pandas._libs import index as libindex +from pandas._libs.lib import no_default from pandas._libs.tslibs import frequencies as libfrequencies, resolution from pandas._libs.tslibs.parsing import parse_time_string from pandas._libs.tslibs.period import Period @@ -248,8 +249,10 @@ def _has_complex_internals(self): # used to avoid libreduction code paths, which raise or require conversion return True - def _shallow_copy(self, values=None, **kwargs): + def _shallow_copy(self, values=None, name=no_default): # TODO: simplify, figure out type of values + name = name if name is not no_default else self.name + if values is None: values = self._data @@ -263,18 +266,7 @@ def _shallow_copy(self, values=None, **kwargs): # GH#30713 this should never be reached raise TypeError(type(values), getattr(values, "dtype", None)) - # We don't allow changing `freq` in _shallow_copy. - validate_dtype_freq(self.dtype, kwargs.get("freq")) - attributes = self._get_attributes_dict() - - attributes.update(kwargs) - if not len(values) and "dtype" not in kwargs: - attributes["dtype"] = self.dtype - return self._simple_new(values, **attributes) - - def _shallow_copy_with_infer(self, values=None, **kwargs): - """ we always want to return a PeriodIndex """ - return self._shallow_copy(values=values, **kwargs) + return self._simple_new(values, name=name) def _maybe_convert_timedelta(self, other): """ diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index d6752da6bc58f..85425e7da03fb 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -7,6 +7,7 @@ import numpy as np from pandas._libs import index as libindex +from pandas._libs.lib import no_default import pandas.compat as compat from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender, cache_readonly @@ -385,13 +386,13 @@ def tolist(self): return list(self._range) @Appender(Int64Index._shallow_copy.__doc__) - def _shallow_copy(self, values=None, **kwargs): + def _shallow_copy(self, values=None, name=no_default): + name = self.name if name is no_default else name + if values is None: - name = kwargs.get("name", self.name) return self._simple_new(self._range, name=name) else: - kwargs.setdefault("name", self.name) - return self._int64index._shallow_copy(values, **kwargs) + return Int64Index._simple_new(values, name=name) @Appender(Int64Index.copy.__doc__) def copy(self, name=None, deep=False, dtype=None, **kwargs): diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 6479b14e9521e..40c7ffba46450 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -128,15 +128,9 @@ def test_shallow_copy_empty(self): def test_shallow_copy_i8(self): # GH-24391 pi = period_range("2018-01-01", periods=3, freq="2D") - result = pi._shallow_copy(pi.asi8, freq=pi.freq) + result = pi._shallow_copy(pi.asi8) tm.assert_index_equal(result, pi) - def test_shallow_copy_changing_freq_raises(self): - pi = period_range("2018-01-01", periods=3, freq="2D") - msg = "specified freq and dtype are different" - with pytest.raises(IncompatibleFrequency, match=msg): - pi._shallow_copy(pi, freq="H") - def test_view_asi8(self): idx = PeriodIndex([], freq="M") From 8fb76cffbc35f4e864d10b5795470bd7c235cf01 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 25 Feb 2020 07:36:29 -0800 Subject: [PATCH 2/3] dtlike shallow_kwargs --- pandas/core/indexes/datetimelike.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 1b3b6934aa53a..e6159cc3575c5 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -649,7 +649,9 @@ def _set_freq(self, freq): self._data._freq = freq - def _shallow_copy(self, values=None, **kwargs): + def _shallow_copy(self, values=None, name=lib.no_default): + name = self.name if name is lib.no_default else name + if values is None: values = self._data @@ -657,18 +659,16 @@ def _shallow_copy(self, values=None, **kwargs): values = values._data if isinstance(values, np.ndarray): # TODO: We would rather not get here - if kwargs.get("freq") is not None: - raise ValueError(kwargs) values = type(self._data)(values, dtype=self.dtype) attributes = self._get_attributes_dict() - if "freq" not in kwargs and self.freq is not None: + if self.freq is not None: if isinstance(values, (DatetimeArray, TimedeltaArray)): if values.freq is None: del attributes["freq"] - attributes.update(kwargs) + attributes["name"] = name return type(self)._simple_new(values, **attributes) # -------------------------------------------------------------------- @@ -738,9 +738,7 @@ def intersection(self, other, sort=False): # this point, depending on the values. result._set_freq(None) - result = self._shallow_copy( - result._data, name=result.name, dtype=result.dtype, freq=None - ) + result = self._shallow_copy(result._data, name=result.name) if result.freq is None: result._set_freq("infer") return result From 8014feca590b944cb1332d73d6b22b99a825482a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 25 Feb 2020 10:28:57 -0800 Subject: [PATCH 3/3] annotate --- pandas/core/indexes/base.py | 4 ++-- pandas/core/indexes/category.py | 3 ++- pandas/core/indexes/datetimelike.py | 3 ++- pandas/core/indexes/numeric.py | 4 ++-- pandas/core/indexes/period.py | 3 ++- pandas/core/indexes/range.py | 3 ++- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 42966d3a335e0..67f2f05c8af1e 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -485,7 +485,7 @@ def _get_attributes_dict(self): """ return {k: getattr(self, k, None) for k in self._attributes} - def _shallow_copy(self, values=None, name=no_default): + def _shallow_copy(self, values=None, name: Label = no_default): """ Create a new Index with the same class as the caller, don't copy the data, use the same object attributes with passed in attributes taking @@ -496,7 +496,7 @@ def _shallow_copy(self, values=None, name=no_default): Parameters ---------- values : the values to create the new Index, optional - kwargs : updates the default attributes for this Index + name : Label, defaults to self.name """ name = self.name if name is no_default else name diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 0bd3b86b25579..603ec486d943e 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -8,6 +8,7 @@ from pandas._libs import index as libindex from pandas._libs.hashtable import duplicated_int64 from pandas._libs.lib import no_default +from pandas._typing import Label from pandas.util._decorators import Appender, cache_readonly from pandas.core.dtypes.common import ( @@ -265,7 +266,7 @@ def _simple_new(cls, values, name=None, dtype=None): # -------------------------------------------------------------------- @Appender(Index._shallow_copy.__doc__) - def _shallow_copy(self, values=None, name=no_default): + def _shallow_copy(self, values=None, name: Label = no_default): name = self.name if name is no_default else name if values is None: diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index e6159cc3575c5..1abd58007c15f 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -8,6 +8,7 @@ from pandas._libs import NaT, iNaT, join as libjoin, lib from pandas._libs.tslibs import timezones +from pandas._typing import Label from pandas.compat.numpy import function as nv from pandas.errors import AbstractMethodError from pandas.util._decorators import Appender, cache_readonly @@ -649,7 +650,7 @@ def _set_freq(self, freq): self._data._freq = freq - def _shallow_copy(self, values=None, name=lib.no_default): + def _shallow_copy(self, values=None, name: Label = lib.no_default): name = self.name if name is lib.no_default else name if values is None: diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 367870f0ee467..06a26cc90555e 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -3,7 +3,7 @@ import numpy as np from pandas._libs import index as libindex, lib -from pandas._typing import Dtype +from pandas._typing import Dtype, Label from pandas.util._decorators import Appender, cache_readonly from pandas.core.dtypes.cast import astype_nansafe @@ -103,7 +103,7 @@ def _maybe_cast_slice_bound(self, label, side, kind): return self._maybe_cast_indexer(label) @Appender(Index._shallow_copy.__doc__) - def _shallow_copy(self, values=None, name=lib.no_default): + def _shallow_copy(self, values=None, name: Label = lib.no_default): name = name if name is not lib.no_default else self.name if values is not None and not self._can_hold_na and values.dtype.kind == "f": diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 2c159e5b4199b..c7c11c60185b3 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -9,6 +9,7 @@ from pandas._libs.tslibs import frequencies as libfrequencies, resolution from pandas._libs.tslibs.parsing import parse_time_string from pandas._libs.tslibs.period import Period +from pandas._typing import Label from pandas.util._decorators import Appender, cache_readonly from pandas.core.dtypes.common import ( @@ -249,7 +250,7 @@ def _has_complex_internals(self): # used to avoid libreduction code paths, which raise or require conversion return True - def _shallow_copy(self, values=None, name=no_default): + def _shallow_copy(self, values=None, name: Label = no_default): # TODO: simplify, figure out type of values name = name if name is not no_default else self.name diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 85425e7da03fb..fa8551bc646a6 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -8,6 +8,7 @@ from pandas._libs import index as libindex from pandas._libs.lib import no_default +from pandas._typing import Label import pandas.compat as compat from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender, cache_readonly @@ -386,7 +387,7 @@ def tolist(self): return list(self._range) @Appender(Int64Index._shallow_copy.__doc__) - def _shallow_copy(self, values=None, name=no_default): + def _shallow_copy(self, values=None, name: Label = no_default): name = self.name if name is no_default else name if values is None: