diff --git a/genome_kit/interval_data.py b/genome_kit/interval_data.py index 607415ae..6ab5e439 100644 --- a/genome_kit/interval_data.py +++ b/genome_kit/interval_data.py @@ -161,8 +161,8 @@ def interval(self) -> IntervalLike: return self._interval @property - def data(self): - """Return the backing data array.""" + def data(self) -> np.ndarray: + """Return the backing data array as read-only.""" if isinstance(self._data, np.ndarray): read_only_view = self._data.view() read_only_view.flags.writeable = False @@ -435,7 +435,7 @@ def _concat_pieces(self, intervals: list[Interval]): """Fetch and concatenate the data slices for ``intervals`` (in order) along the aligned axis. """ - pieces = [self._get_interval_like(iv).data for iv in intervals] + pieces = [self._get_interval_like(iv)._data for iv in intervals] return pieces[0] if len(pieces) == 1 else np.concatenate(pieces, axis=self._axis) # The forms a value assigned via an interval-like key may take. See diff --git a/tests/test_interval_data.py b/tests/test_interval_data.py index 41aec87c..c10bba51 100644 --- a/tests/test_interval_data.py +++ b/tests/test_interval_data.py @@ -155,6 +155,36 @@ def test_set_dis(self): interval_data[0] = -interval_data[0] np.testing.assert_equal(-data[0], interval_data[0]) + def test_data_read_only_does_not_affect_backing_array(self): + position = Interval('chr1', '+', 100, 100, self.genome) + interval = position.expand(0, 3) + data = np.arange(0, 30).reshape(3, 10) + interval_data = IntervalData(interval, data) + + view = interval_data.data + self.assertFalse(view.flags.writeable) + with self.assertRaises(ValueError): + view[0, 0] = 1 + + self.assertTrue(data.flags.writeable) + interval_data[0] = -interval_data[0] + np.testing.assert_equal(-np.arange(0, 10), interval_data[0]) + + def test_set_after_single_piece_concat(self): + base = Interval('chr1', '+', 100, 106, self.genome) + piece = Interval('chr1', '+', 101, 103, self.genome) + keys = { + 'list': [piece], + 'dis': DisjointIntervalSequence.from_intervals([piece]), + } + for name, key in keys.items(): + with self.subTest(name): + interval_data = IntervalData(base, np.zeros((6, 4), dtype=int)) + sub = interval_data[key] + sub[0] = 1 + np.testing.assert_equal(np.ones(4), sub[0]) + np.testing.assert_equal(np.ones(4), interval_data[1]) + class TestIntervalDataSliceIndex(unittest.TestCase): rank = 3