Skip to content

Commit

Permalink
BUG: raise consistent exception on slicing failure (#38077)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored Nov 26, 2020
1 parent 090d6a1 commit fa999f3
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 10 deletions.
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

0 comments on commit fa999f3

Please sign in to comment.