Skip to content

REF: _validate_partial_date_slice -> _can_partial_date_slice #42225

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def _summary(self, name=None) -> str:
# --------------------------------------------------------------------
# Indexing Methods

def _validate_partial_date_slice(self, reso: Resolution):
def _can_partial_date_slice(self, reso: Resolution) -> bool:
raise NotImplementedError

def _parsed_string_to_bounds(self, reso: Resolution, parsed):
Expand Down Expand Up @@ -317,7 +317,8 @@ def _partial_date_slice(
-------
slice or ndarray[intp]
"""
self._validate_partial_date_slice(reso)
if not self._can_partial_date_slice(reso):
raise ValueError

t1, t2 = self._parsed_string_to_bounds(reso, parsed)
vals = self._data._ndarray
Expand Down
21 changes: 15 additions & 6 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
end = end.tz_localize(self.tz)
return start, end

def _validate_partial_date_slice(self, reso: Resolution):
def _can_partial_date_slice(self, reso: Resolution) -> bool:
assert isinstance(reso, Resolution), (type(reso), reso)
if (
self.is_monotonic
Expand All @@ -614,12 +614,13 @@ def _validate_partial_date_slice(self, reso: Resolution):
# GH3452 and GH2369.

# See also GH14826
raise KeyError
return False

if reso.attrname == "microsecond":
# _partial_date_slice doesn't allow microsecond resolution, but
# _parsed_string_to_bounds allows it.
raise KeyError
return False
return True

def _deprecate_mismatched_indexing(self, key) -> None:
# GH#36148
Expand Down Expand Up @@ -663,14 +664,22 @@ def get_loc(self, key, method=None, tolerance=None):
key = self._maybe_cast_for_get_loc(key)

elif isinstance(key, str):

try:
return self._get_string_slice(key)
except (TypeError, KeyError, ValueError, OverflowError):
pass
parsed, reso = self._parse_with_reso(key)
except ValueError as err:
raise KeyError(key) from err

if self._can_partial_date_slice(reso):
try:
return self._partial_date_slice(reso, parsed)
except KeyError as err:
if method is None:
raise KeyError(key) from err
try:
key = self._maybe_cast_for_get_loc(key)
except ValueError as err:
# FIXME: we get here because parse_with_reso doesn't raise on "t2m"
raise KeyError(key) from err

elif isinstance(key, timedelta):
Expand Down
59 changes: 33 additions & 26 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@
)
from pandas._libs.tslibs import (
BaseOffset,
NaT,
Period,
Resolution,
Tick,
)
from pandas._libs.tslibs.parsing import (
DateParseError,
parse_time_string,
)
from pandas._typing import (
Dtype,
DtypeObj,
Expand All @@ -35,6 +32,7 @@
pandas_dtype,
)
from pandas.core.dtypes.dtypes import PeriodDtype
from pandas.core.dtypes.missing import is_valid_na_for_dtype

from pandas.core.arrays.period import (
PeriodArray,
Expand Down Expand Up @@ -409,43 +407,50 @@ def get_loc(self, key, method=None, tolerance=None):
orig_key = key

self._check_indexing_error(key)
if isinstance(key, str):

try:
loc = self._get_string_slice(key)
return loc
except (TypeError, ValueError):
pass
if is_valid_na_for_dtype(key, self.dtype):
key = NaT

elif isinstance(key, str):

try:
asdt, reso_str = parse_time_string(key, self.freq)
except (ValueError, DateParseError) as err:
parsed, reso = self._parse_with_reso(key)
except ValueError as err:
# A string with invalid format
raise KeyError(f"Cannot interpret '{key}' as period") from err

reso = Resolution.from_attrname(reso_str)
if self._can_partial_date_slice(reso):
try:
return self._partial_date_slice(reso, parsed)
except KeyError as err:
# TODO: pass if method is not None, like DTI does?
raise KeyError(key) from err

if reso == self.dtype.resolution:
# the reso < self.dtype.resolution case goes through _get_string_slice
key = Period(asdt, freq=self.freq)
key = Period(parsed, freq=self.freq)
loc = self.get_loc(key, method=method, tolerance=tolerance)
# Recursing instead of falling through matters for the exception
# message in test_get_loc3 (though not clear if that really matters)
return loc
elif method is None:
raise KeyError(key)
else:
key = asdt
key = Period(parsed, freq=self.freq)

elif is_integer(key):
# Period constructor will cast to string, which we dont want
raise KeyError(key)
elif isinstance(key, Period) and key.freq != self.freq:
raise KeyError(key)

try:
key = Period(key, freq=self.freq)
except ValueError as err:
# we cannot construct the Period
raise KeyError(orig_key) from err
elif isinstance(key, Period):
pass
elif isinstance(key, datetime):
try:
key = Period(key, freq=self.freq)
except ValueError as err:
# we cannot construct the Period
raise KeyError(orig_key) from err
else:
# in particular integer, which Period constructor would cast to string
raise KeyError(key)

try:
return Index.get_loc(self, key, method, tolerance)
Expand Down Expand Up @@ -496,7 +501,7 @@ def _parsed_string_to_bounds(self, reso: Resolution, parsed: datetime):
iv = Period(parsed, freq=grp.value)
return (iv.asfreq(self.freq, how="start"), iv.asfreq(self.freq, how="end"))

def _validate_partial_date_slice(self, reso: Resolution):
def _can_partial_date_slice(self, reso: Resolution) -> bool:
assert isinstance(reso, Resolution), (type(reso), reso)
grp = reso.freq_group
freqn = self.dtype.freq_group_code
Expand All @@ -505,7 +510,9 @@ def _validate_partial_date_slice(self, reso: Resolution):
# TODO: we used to also check for
# reso in ["day", "hour", "minute", "second"]
# why is that check not needed?
raise ValueError
return False

return True


def period_range(
Expand Down