Skip to content

BUG: raise consistent exception on slicing failure #38077

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 2 commits into from
Nov 26, 2020
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
8 changes: 4 additions & 4 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3433,11 +3433,11 @@ def _convert_list_indexer(self, keyarr):
return None

@final
def _invalid_indexer(self, form: str_t, key):
def _invalid_indexer(self, form: str_t, key) -> TypeError:
"""
Consistent invalid indexer message.
"""
raise TypeError(
return TypeError(
f"cannot do {form} indexing on {type(self).__name__} with these "
f"indexers [{key}] of type {type(key).__name__}"
)
Expand Down Expand Up @@ -5238,7 +5238,7 @@ def _validate_indexer(self, form: str_t, key, kind: str_t):
elif is_integer(key):
pass
else:
self._invalid_indexer(form, key)
raise self._invalid_indexer(form, key)

def _maybe_cast_slice_bound(self, label, side: str_t, kind):
"""
Expand Down Expand Up @@ -5267,7 +5267,7 @@ def _maybe_cast_slice_bound(self, label, side: str_t, kind):
# datetimelike Indexes
# reject them, if index does not contain label
if (is_float(label) or is_integer(label)) and label not in self.values:
self._invalid_indexer("slice", label)
raise self._invalid_indexer("slice", label)

return label

Expand Down
8 changes: 6 additions & 2 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,11 @@ def _maybe_cast_slice_bound(self, label, side: str, kind):

if isinstance(label, str):
freq = getattr(self, "freqstr", getattr(self, "inferred_freq", None))
parsed, reso = parsing.parse_time_string(label, freq)
try:
parsed, reso = parsing.parse_time_string(label, freq)
except parsing.DateParseError as err:
raise self._invalid_indexer("slice", label) from err

reso = Resolution.from_attrname(reso)
lower, upper = self._parsed_string_to_bounds(reso, parsed)
# lower, upper form the half-open interval:
Expand All @@ -732,7 +736,7 @@ def _maybe_cast_slice_bound(self, label, side: str, kind):
elif isinstance(label, (self._data._recognized_scalars, date)):
self._deprecate_mismatched_indexing(label)
else:
self._invalid_indexer("slice", label)
raise self._invalid_indexer("slice", label)

return self._maybe_cast_for_get_loc(label)

Expand Down
5 changes: 2 additions & 3 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,9 @@ def _maybe_cast_slice_bound(self, label, side: str, kind: str):
return bounds[0 if side == "left" else 1]
except ValueError as err:
# string cannot be parsed as datetime-like
# TODO: we need tests for this case
raise KeyError(label) from err
raise self._invalid_indexer("slice", label) from err
elif is_integer(label) or is_float(label):
self._invalid_indexer("slice", label)
raise self._invalid_indexer("slice", label)

return label

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexes/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def _maybe_cast_slice_bound(self, label, side: str, kind):
else:
return lbound + to_offset(parsed.resolution_string) - Timedelta(1, "ns")
elif not isinstance(label, self._data._recognized_scalars):
self._invalid_indexer("slice", label)
raise self._invalid_indexer("slice", label)

return label

Expand Down
28 changes: 28 additions & 0 deletions pandas/tests/indexes/period/test_partial_slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,34 @@ def test_range_slice_outofbounds(self, make_range):
tm.assert_frame_equal(df["2013-06":"2013-09"], empty)
tm.assert_frame_equal(df["2013-11":"2013-12"], empty)

@pytest.mark.parametrize("make_range", [date_range, period_range])
def test_maybe_cast_slice_bound(self, make_range, frame_or_series):
idx = make_range(start="2013/10/01", freq="D", periods=10)

obj = DataFrame(dict(units=[100 + i for i in range(10)]), index=idx)
if frame_or_series is not DataFrame:
obj = obj["units"]

msg = (
f"cannot do slice indexing on {type(idx).__name__} with "
r"these indexers \[foo\] of type str"
)

# Check the lower-level calls are raising where expected.
with pytest.raises(TypeError, match=msg):
idx._maybe_cast_slice_bound("foo", "left", "loc")
with pytest.raises(TypeError, match=msg):
idx.get_slice_bound("foo", "left", "loc")

with pytest.raises(TypeError, match=msg):
obj["2013/09/30":"foo"]
with pytest.raises(TypeError, match=msg):
obj["foo":"2013/09/30"]
with pytest.raises(TypeError, match=msg):
obj.loc["2013/09/30":"foo"]
with pytest.raises(TypeError, match=msg):
obj.loc["foo":"2013/09/30"]

def test_partial_slice_doesnt_require_monotonicity(self):
# See also: DatetimeIndex test ofm the same name
dti = date_range("2014-01-01", periods=30, freq="30D")
Expand Down