From ac3f42253767ef3bc6dd9320248da04b8ed259a1 Mon Sep 17 00:00:00 2001 From: Brock Date: Wed, 4 Aug 2021 19:42:48 -0700 Subject: [PATCH 1/2] REF: share maybe_cast_slice_bound DTI/TDI/PI --- pandas/core/indexes/base.py | 3 +- pandas/core/indexes/datetimelike.py | 37 ++++++++++++++++++++++++ pandas/core/indexes/datetimes.py | 45 ++--------------------------- pandas/core/indexes/period.py | 38 ++---------------------- pandas/core/indexes/timedeltas.py | 31 -------------------- 5 files changed, 45 insertions(+), 109 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 1c94baf74b60b..a3e45c972d63f 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -57,6 +57,7 @@ deprecate_nonkeyword_arguments, doc, ) +from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.cast import ( can_hold_element, @@ -6522,7 +6523,7 @@ def _deprecated_arg(self, value, name: str_t, methodname: str_t) -> None: f"'{name}' argument in {methodname} is deprecated " "and will be removed in a future version. Do not pass it.", FutureWarning, - stacklevel=3, + stacklevel=find_stack_level(), ) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 7dc59bdb1e840..b7f282e43517b 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -345,6 +345,43 @@ def _partial_date_slice( # try to find the dates return (lhs_mask & rhs_mask).nonzero()[0] + def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default): + """ + If label is a string, cast it to scalar type according to resolution. + + Parameters + ---------- + label : object + side : {'left', 'right'} + kind : {'loc', 'getitem'} or None + + Returns + ------- + label : object + + Notes + ----- + Value of `side` parameter should be validated in caller. + """ + assert kind in ["loc", "getitem", None, lib.no_default] + self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound") + + if isinstance(label, str): + try: + parsed, reso = self._parse_with_reso(label) + except ValueError as err: + # DTI -> parsing.DateParseError + # TDI -> 'unit abbreviation w/o a number' + # PI -> string cannot be parsed as datetime-like + raise self._invalid_indexer("slice", label) from err + + lower, upper = self._parsed_string_to_bounds(reso, parsed) + return lower if side == "left" else upper + elif not isinstance(label, self._data._recognized_scalars): + raise self._invalid_indexer("slice", label) + + return label + # -------------------------------------------------------------------- # Arithmetic Methods diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 97c648013f9d1..df62701fa1d28 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -25,7 +25,6 @@ ) from pandas._libs.tslibs import ( Resolution, - parsing, timezones, to_offset, ) @@ -685,48 +684,10 @@ def _maybe_cast_for_get_loc(self, key) -> Timestamp: key = key.tz_convert(self.tz) return key + @doc(DatetimeTimedeltaMixin._maybe_cast_slice_bound) def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default): - """ - If label is a string, cast it to datetime according to resolution. - - Parameters - ---------- - label : object - side : {'left', 'right'} - kind : {'loc', 'getitem'} or None - - Returns - ------- - label : object - - Notes - ----- - Value of `side` parameter should be validated in caller. - """ - assert kind in ["loc", "getitem", None, lib.no_default] - self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound") - - if isinstance(label, str): - try: - parsed, reso = self._parse_with_reso(label) - except parsing.DateParseError as err: - raise self._invalid_indexer("slice", label) from err - - lower, upper = self._parsed_string_to_bounds(reso, parsed) - # lower, upper form the half-open interval: - # [parsed, parsed + 1 freq) - # because label may be passed to searchsorted - # the bounds need swapped if index is reverse sorted and has a - # length > 1 (is_monotonic_decreasing gives True for empty - # and length 1 index) - if self._is_strictly_monotonic_decreasing and len(self) > 1: - return upper if side == "left" else lower - return lower if side == "left" else upper - elif isinstance(label, self._data._recognized_scalars): - self._deprecate_mismatched_indexing(label) - else: - raise self._invalid_indexer("slice", label) - + label = super()._maybe_cast_slice_bound(label, side, kind=kind) + self._deprecate_mismatched_indexing(label) return self._maybe_cast_for_get_loc(label) def slice_indexer(self, start=None, end=None, step=None, kind=lib.no_default): diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index df3862553a70c..4e47a59740212 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -464,44 +464,12 @@ def get_loc(self, key, method=None, tolerance=None): except KeyError as err: raise KeyError(orig_key) from err + @doc(DatetimeIndexOpsMixin._maybe_cast_slice_bound) def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default): - """ - If label is a string or a datetime, cast it to Period.ordinal according - to resolution. - - Parameters - ---------- - label : object - side : {'left', 'right'} - kind : {'loc', 'getitem'}, or None - - Returns - ------- - bound : Period or object - - Notes - ----- - Value of `side` parameter should be validated in caller. - - """ - assert kind in ["loc", "getitem", None, lib.no_default] - self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound") - if isinstance(label, datetime): - return Period(label, freq=self.freq) - elif isinstance(label, str): - try: - parsed, reso = self._parse_with_reso(label) - except ValueError as err: - # string cannot be parsed as datetime-like - raise self._invalid_indexer("slice", label) from err - - lower, upper = self._parsed_string_to_bounds(reso, parsed) - return lower if side == "left" else upper - elif not isinstance(label, self._data._recognized_scalars): - raise self._invalid_indexer("slice", label) + label = Period(label, freq=self.freq) - return label + return super()._maybe_cast_slice_bound(label, side, kind=kind) def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime): grp = reso.freq_group diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 023cb651c9632..0249bf51f71b7 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -178,37 +178,6 @@ def get_loc(self, key, method=None, tolerance=None): return Index.get_loc(self, key, method, tolerance) - def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default): - """ - If label is a string, cast it to timedelta according to resolution. - - Parameters - ---------- - label : object - side : {'left', 'right'} - kind : {'loc', 'getitem'} or None - - Returns - ------- - label : object - """ - assert kind in ["loc", "getitem", None, lib.no_default] - self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound") - - if isinstance(label, str): - try: - parsed, reso = self._parse_with_reso(label) - except ValueError as err: - # e.g. 'unit abbreviation w/o a number' - raise self._invalid_indexer("slice", label) from err - - lower, upper = self._parsed_string_to_bounds(reso, parsed) - return lower if side == "left" else upper - elif not isinstance(label, self._data._recognized_scalars): - raise self._invalid_indexer("slice", label) - - return label - def _parse_with_reso(self, label: str): # the "with_reso" is a no-op for TimedeltaIndex parsed = Timedelta(label) From 45290b29a3e0f83a4d3f8cfd1dda6993671be1ca Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 5 Aug 2021 07:19:06 -0700 Subject: [PATCH 2/2] fix warning --- pandas/core/indexes/datetimes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index df62701fa1d28..5379a23df19cf 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -764,7 +764,7 @@ def check_str_or_none(point): return indexer @doc(Index.get_slice_bound) - def get_slice_bound(self, label, side: str, kind=None) -> int: + def get_slice_bound(self, label, side: str, kind=lib.no_default) -> int: # GH#42855 handle date here instead of _maybe_cast_slice_bound if isinstance(label, date) and not isinstance(label, datetime): label = Timestamp(label).to_pydatetime()