Skip to content

Commit

Permalink
REF: share maybe_cast_slice_bound DTI/TDI/PI (#42897)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored Aug 5, 2021
1 parent cc5eba9 commit ae8321d
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 110 deletions.
3 changes: 2 additions & 1 deletion pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -6528,7 +6529,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(),
)


Expand Down
37 changes: 37 additions & 0 deletions pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
47 changes: 4 additions & 43 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
)
from pandas._libs.tslibs import (
Resolution,
parsing,
timezones,
to_offset,
)
Expand Down Expand Up @@ -686,48 +685,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):
Expand Down Expand Up @@ -804,7 +765,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()
Expand Down
38 changes: 3 additions & 35 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 0 additions & 31 deletions pandas/core/indexes/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit ae8321d

Please sign in to comment.