Skip to content

Commit d38dc06

Browse files
authored
REF: de-duplicate IntervalArray._validate_foo (#36483)
1 parent c33c3c0 commit d38dc06

File tree

3 files changed

+39
-34
lines changed

3 files changed

+39
-34
lines changed

pandas/core/arrays/interval.py

+31-31
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
is_datetime64_any_dtype,
2121
is_float_dtype,
2222
is_integer_dtype,
23-
is_interval,
2423
is_interval_dtype,
2524
is_list_like,
2625
is_object_dtype,
@@ -813,7 +812,9 @@ def take(self, indices, allow_fill=False, fill_value=None, axis=None, **kwargs):
813812

814813
fill_left = fill_right = fill_value
815814
if allow_fill:
816-
fill_left, fill_right = self._validate_fill_value(fill_value)
815+
if (np.asarray(indices) == -1).any():
816+
# We have excel tests that pass fill_value=True, xref GH#36466
817+
fill_left, fill_right = self._validate_fill_value(fill_value)
817818

818819
left_take = take(
819820
self.left, indices, allow_fill=allow_fill, fill_value=fill_left
@@ -824,20 +825,33 @@ def take(self, indices, allow_fill=False, fill_value=None, axis=None, **kwargs):
824825

825826
return self._shallow_copy(left_take, right_take)
826827

827-
def _validate_fill_value(self, value):
828-
if is_interval(value):
829-
self._check_closed_matches(value, name="fill_value")
830-
fill_left, fill_right = value.left, value.right
831-
elif not is_scalar(value) and notna(value):
832-
msg = (
833-
"'IntervalArray.fillna' only supports filling with a "
834-
"'scalar pandas.Interval or NA'. "
835-
f"Got a '{type(value).__name__}' instead."
836-
)
837-
raise ValueError(msg)
828+
def _validate_listlike(self, value):
829+
# list-like of intervals
830+
try:
831+
array = IntervalArray(value)
832+
# TODO: self._check_closed_matches(array, name="value")
833+
value_left, value_right = array.left, array.right
834+
except TypeError as err:
835+
# wrong type: not interval or NA
836+
msg = f"'value' should be an interval type, got {type(value)} instead."
837+
raise TypeError(msg) from err
838+
return value_left, value_right
839+
840+
def _validate_scalar(self, value):
841+
if isinstance(value, Interval):
842+
self._check_closed_matches(value, name="value")
843+
left, right = value.left, value.right
844+
elif is_valid_nat_for_dtype(value, self.left.dtype):
845+
# GH#18295
846+
left = right = value
838847
else:
839-
fill_left = fill_right = self.left._na_value
840-
return fill_left, fill_right
848+
raise ValueError(
849+
"can only insert Interval objects and NA into an IntervalArray"
850+
)
851+
return left, right
852+
853+
def _validate_fill_value(self, value):
854+
return self._validate_scalar(value)
841855

842856
def _validate_fillna_value(self, value):
843857
if not isinstance(value, Interval):
@@ -851,26 +865,12 @@ def _validate_fillna_value(self, value):
851865
return value.left, value.right
852866

853867
def _validate_insert_value(self, value):
854-
if isinstance(value, Interval):
855-
if value.closed != self.closed:
856-
raise ValueError(
857-
"inserted item must be closed on the same side as the index"
858-
)
859-
left_insert = value.left
860-
right_insert = value.right
861-
elif is_valid_nat_for_dtype(value, self.left.dtype):
862-
# GH#18295
863-
left_insert = right_insert = value
864-
else:
865-
raise ValueError(
866-
"can only insert Interval objects and NA into an IntervalIndex"
867-
)
868-
return left_insert, right_insert
868+
return self._validate_scalar(value)
869869

870870
def _validate_setitem_value(self, value):
871871
needs_float_conversion = False
872872

873-
if is_scalar(value) and isna(value):
873+
if is_valid_nat_for_dtype(value, self.left.dtype):
874874
# na value: need special casing to set directly on numpy arrays
875875
if is_integer_dtype(self.dtype.subtype):
876876
# can't set NaN on a numpy integer array

pandas/tests/arrays/interval/test_interval.py

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ def test_set_na(self, left_right_dtypes):
105105
left, right = left_right_dtypes
106106
result = IntervalArray.from_arrays(left, right)
107107

108+
if result.dtype.subtype.kind not in ["m", "M"]:
109+
msg = "'value' should be an interval type, got <.*NaTType'> instead."
110+
with pytest.raises(TypeError, match=msg):
111+
result[0] = pd.NaT
108112
if result.dtype.subtype.kind in ["i", "u"]:
109113
msg = "Cannot set float NaN to integer-backed IntervalArray"
110114
with pytest.raises(ValueError, match=msg):

pandas/tests/indexes/interval/test_interval.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,14 @@ def test_insert(self, data):
191191
tm.assert_index_equal(result, expected)
192192

193193
# invalid type
194-
msg = "can only insert Interval objects and NA into an IntervalIndex"
194+
msg = "can only insert Interval objects and NA into an IntervalArray"
195195
with pytest.raises(ValueError, match=msg):
196196
data.insert(1, "foo")
197197

198198
# invalid closed
199-
msg = "inserted item must be closed on the same side as the index"
199+
msg = "'value.closed' is 'left', expected 'right'."
200200
for closed in {"left", "right", "both", "neither"} - {item.closed}:
201+
msg = f"'value.closed' is '{closed}', expected '{item.closed}'."
201202
with pytest.raises(ValueError, match=msg):
202203
bad_item = Interval(item.left, item.right, closed=closed)
203204
data.insert(1, bad_item)
@@ -211,7 +212,7 @@ def test_insert(self, data):
211212

212213
if data.left.dtype.kind not in ["m", "M"]:
213214
# trying to insert pd.NaT into a numeric-dtyped Index should cast/raise
214-
msg = "can only insert Interval objects and NA into an IntervalIndex"
215+
msg = "can only insert Interval objects and NA into an IntervalArray"
215216
with pytest.raises(ValueError, match=msg):
216217
result = data.insert(1, pd.NaT)
217218
else:

0 commit comments

Comments
 (0)