Skip to content

Commit 4334482

Browse files
authored
BUG/API: prohibit dtype-changing IntervalArray.__setitem__ (#32782)
1 parent 6e3537d commit 4334482

File tree

4 files changed

+25
-11
lines changed

4 files changed

+25
-11
lines changed

doc/source/whatsnew/v1.1.0.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ Strings
431431

432432
Interval
433433
^^^^^^^^
434-
-
434+
- Bug in :class:`IntervalArray` incorrectly allowing the underlying data to be changed when setting values (:issue:`32782`)
435435
-
436436

437437
Indexing

pandas/core/arrays/interval.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -542,19 +542,19 @@ def __setitem__(self, key, value):
542542
msg = f"'value' should be an interval type, got {type(value)} instead."
543543
raise TypeError(msg) from err
544544

545+
if needs_float_conversion:
546+
raise ValueError("Cannot set float NaN to integer-backed IntervalArray")
547+
545548
key = check_array_indexer(self, key)
549+
546550
# Need to ensure that left and right are updated atomically, so we're
547551
# forced to copy, update the copy, and swap in the new values.
548552
left = self.left.copy(deep=True)
549-
if needs_float_conversion:
550-
left = left.astype("float")
551-
left.values[key] = value_left
553+
left._values[key] = value_left
552554
self._left = left
553555

554556
right = self.right.copy(deep=True)
555-
if needs_float_conversion:
556-
right = right.astype("float")
557-
right.values[key] = value_right
557+
right._values[key] = value_right
558558
self._right = right
559559

560560
def __eq__(self, other):

pandas/tests/arrays/interval/test_interval.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ class TestSetitem:
104104
def test_set_na(self, left_right_dtypes):
105105
left, right = left_right_dtypes
106106
result = IntervalArray.from_arrays(left, right)
107+
108+
if result.dtype.subtype.kind in ["i", "u"]:
109+
msg = "Cannot set float NaN to integer-backed IntervalArray"
110+
with pytest.raises(ValueError, match=msg):
111+
result[0] = np.NaN
112+
return
113+
107114
result[0] = np.nan
108115

109116
expected_left = Index([left._na_value] + list(left[1:]))
@@ -182,7 +189,7 @@ def test_arrow_array_missing():
182189
import pyarrow as pa
183190
from pandas.core.arrays._arrow_utils import ArrowIntervalType
184191

185-
arr = IntervalArray.from_breaks([0, 1, 2, 3])
192+
arr = IntervalArray.from_breaks([0.0, 1.0, 2.0, 3.0])
186193
arr[1] = None
187194

188195
result = pa.array(arr)
@@ -209,8 +216,8 @@ def test_arrow_array_missing():
209216
@pyarrow_skip
210217
@pytest.mark.parametrize(
211218
"breaks",
212-
[[0, 1, 2, 3], pd.date_range("2017", periods=4, freq="D")],
213-
ids=["int", "datetime64[ns]"],
219+
[[0.0, 1.0, 2.0, 3.0], pd.date_range("2017", periods=4, freq="D")],
220+
ids=["float", "datetime64[ns]"],
214221
)
215222
def test_arrow_table_roundtrip(breaks):
216223
import pyarrow as pa

pandas/tests/series/methods/test_convert_dtypes.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import numpy as np
44
import pytest
55

6+
from pandas.core.dtypes.common import is_interval_dtype
7+
68
import pandas as pd
79
import pandas._testing as tm
810

@@ -266,7 +268,12 @@ def test_convert_dtypes(self, data, maindtype, params, answerdict):
266268

267269
# Test that it is a copy
268270
copy = series.copy(deep=True)
269-
ns[ns.notna()] = np.nan
271+
if is_interval_dtype(ns.dtype) and ns.dtype.subtype.kind in ["i", "u"]:
272+
msg = "Cannot set float NaN to integer-backed IntervalArray"
273+
with pytest.raises(ValueError, match=msg):
274+
ns[ns.notna()] = np.nan
275+
else:
276+
ns[ns.notna()] = np.nan
270277

271278
# Make sure original not changed
272279
tm.assert_series_equal(series, copy)

0 commit comments

Comments
 (0)