Skip to content

Commit ae8321d

Browse files
authored
REF: share maybe_cast_slice_bound DTI/TDI/PI (#42897)
1 parent cc5eba9 commit ae8321d

File tree

5 files changed

+46
-110
lines changed

5 files changed

+46
-110
lines changed

pandas/core/indexes/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
deprecate_nonkeyword_arguments,
5858
doc,
5959
)
60+
from pandas.util._exceptions import find_stack_level
6061

6162
from pandas.core.dtypes.cast import (
6263
can_hold_element,
@@ -6528,7 +6529,7 @@ def _deprecated_arg(self, value, name: str_t, methodname: str_t) -> None:
65286529
f"'{name}' argument in {methodname} is deprecated "
65296530
"and will be removed in a future version. Do not pass it.",
65306531
FutureWarning,
6531-
stacklevel=3,
6532+
stacklevel=find_stack_level(),
65326533
)
65336534

65346535

pandas/core/indexes/datetimelike.py

+37
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,43 @@ def _partial_date_slice(
345345
# try to find the dates
346346
return (lhs_mask & rhs_mask).nonzero()[0]
347347

348+
def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
349+
"""
350+
If label is a string, cast it to scalar type according to resolution.
351+
352+
Parameters
353+
----------
354+
label : object
355+
side : {'left', 'right'}
356+
kind : {'loc', 'getitem'} or None
357+
358+
Returns
359+
-------
360+
label : object
361+
362+
Notes
363+
-----
364+
Value of `side` parameter should be validated in caller.
365+
"""
366+
assert kind in ["loc", "getitem", None, lib.no_default]
367+
self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound")
368+
369+
if isinstance(label, str):
370+
try:
371+
parsed, reso = self._parse_with_reso(label)
372+
except ValueError as err:
373+
# DTI -> parsing.DateParseError
374+
# TDI -> 'unit abbreviation w/o a number'
375+
# PI -> string cannot be parsed as datetime-like
376+
raise self._invalid_indexer("slice", label) from err
377+
378+
lower, upper = self._parsed_string_to_bounds(reso, parsed)
379+
return lower if side == "left" else upper
380+
elif not isinstance(label, self._data._recognized_scalars):
381+
raise self._invalid_indexer("slice", label)
382+
383+
return label
384+
348385
# --------------------------------------------------------------------
349386
# Arithmetic Methods
350387

pandas/core/indexes/datetimes.py

+4-43
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
)
2626
from pandas._libs.tslibs import (
2727
Resolution,
28-
parsing,
2928
timezones,
3029
to_offset,
3130
)
@@ -686,48 +685,10 @@ def _maybe_cast_for_get_loc(self, key) -> Timestamp:
686685
key = key.tz_convert(self.tz)
687686
return key
688687

688+
@doc(DatetimeTimedeltaMixin._maybe_cast_slice_bound)
689689
def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
690-
"""
691-
If label is a string, cast it to datetime according to resolution.
692-
693-
Parameters
694-
----------
695-
label : object
696-
side : {'left', 'right'}
697-
kind : {'loc', 'getitem'} or None
698-
699-
Returns
700-
-------
701-
label : object
702-
703-
Notes
704-
-----
705-
Value of `side` parameter should be validated in caller.
706-
"""
707-
assert kind in ["loc", "getitem", None, lib.no_default]
708-
self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound")
709-
710-
if isinstance(label, str):
711-
try:
712-
parsed, reso = self._parse_with_reso(label)
713-
except parsing.DateParseError as err:
714-
raise self._invalid_indexer("slice", label) from err
715-
716-
lower, upper = self._parsed_string_to_bounds(reso, parsed)
717-
# lower, upper form the half-open interval:
718-
# [parsed, parsed + 1 freq)
719-
# because label may be passed to searchsorted
720-
# the bounds need swapped if index is reverse sorted and has a
721-
# length > 1 (is_monotonic_decreasing gives True for empty
722-
# and length 1 index)
723-
if self._is_strictly_monotonic_decreasing and len(self) > 1:
724-
return upper if side == "left" else lower
725-
return lower if side == "left" else upper
726-
elif isinstance(label, self._data._recognized_scalars):
727-
self._deprecate_mismatched_indexing(label)
728-
else:
729-
raise self._invalid_indexer("slice", label)
730-
690+
label = super()._maybe_cast_slice_bound(label, side, kind=kind)
691+
self._deprecate_mismatched_indexing(label)
731692
return self._maybe_cast_for_get_loc(label)
732693

733694
def slice_indexer(self, start=None, end=None, step=None, kind=lib.no_default):
@@ -804,7 +765,7 @@ def check_str_or_none(point):
804765
return indexer
805766

806767
@doc(Index.get_slice_bound)
807-
def get_slice_bound(self, label, side: str, kind=None) -> int:
768+
def get_slice_bound(self, label, side: str, kind=lib.no_default) -> int:
808769
# GH#42855 handle date here instead of _maybe_cast_slice_bound
809770
if isinstance(label, date) and not isinstance(label, datetime):
810771
label = Timestamp(label).to_pydatetime()

pandas/core/indexes/period.py

+3-35
Original file line numberDiff line numberDiff line change
@@ -464,44 +464,12 @@ def get_loc(self, key, method=None, tolerance=None):
464464
except KeyError as err:
465465
raise KeyError(orig_key) from err
466466

467+
@doc(DatetimeIndexOpsMixin._maybe_cast_slice_bound)
467468
def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
468-
"""
469-
If label is a string or a datetime, cast it to Period.ordinal according
470-
to resolution.
471-
472-
Parameters
473-
----------
474-
label : object
475-
side : {'left', 'right'}
476-
kind : {'loc', 'getitem'}, or None
477-
478-
Returns
479-
-------
480-
bound : Period or object
481-
482-
Notes
483-
-----
484-
Value of `side` parameter should be validated in caller.
485-
486-
"""
487-
assert kind in ["loc", "getitem", None, lib.no_default]
488-
self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound")
489-
490469
if isinstance(label, datetime):
491-
return Period(label, freq=self.freq)
492-
elif isinstance(label, str):
493-
try:
494-
parsed, reso = self._parse_with_reso(label)
495-
except ValueError as err:
496-
# string cannot be parsed as datetime-like
497-
raise self._invalid_indexer("slice", label) from err
498-
499-
lower, upper = self._parsed_string_to_bounds(reso, parsed)
500-
return lower if side == "left" else upper
501-
elif not isinstance(label, self._data._recognized_scalars):
502-
raise self._invalid_indexer("slice", label)
470+
label = Period(label, freq=self.freq)
503471

504-
return label
472+
return super()._maybe_cast_slice_bound(label, side, kind=kind)
505473

506474
def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
507475
grp = reso.freq_group

pandas/core/indexes/timedeltas.py

-31
Original file line numberDiff line numberDiff line change
@@ -178,37 +178,6 @@ def get_loc(self, key, method=None, tolerance=None):
178178

179179
return Index.get_loc(self, key, method, tolerance)
180180

181-
def _maybe_cast_slice_bound(self, label, side: str, kind=lib.no_default):
182-
"""
183-
If label is a string, cast it to timedelta according to resolution.
184-
185-
Parameters
186-
----------
187-
label : object
188-
side : {'left', 'right'}
189-
kind : {'loc', 'getitem'} or None
190-
191-
Returns
192-
-------
193-
label : object
194-
"""
195-
assert kind in ["loc", "getitem", None, lib.no_default]
196-
self._deprecated_arg(kind, "kind", "_maybe_cast_slice_bound")
197-
198-
if isinstance(label, str):
199-
try:
200-
parsed, reso = self._parse_with_reso(label)
201-
except ValueError as err:
202-
# e.g. 'unit abbreviation w/o a number'
203-
raise self._invalid_indexer("slice", label) from err
204-
205-
lower, upper = self._parsed_string_to_bounds(reso, parsed)
206-
return lower if side == "left" else upper
207-
elif not isinstance(label, self._data._recognized_scalars):
208-
raise self._invalid_indexer("slice", label)
209-
210-
return label
211-
212181
def _parse_with_reso(self, label: str):
213182
# the "with_reso" is a no-op for TimedeltaIndex
214183
parsed = Timedelta(label)

0 commit comments

Comments
 (0)