From 911d9a6aa5c11db1ff57baf5afcb01899f54713d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:34:33 +0000 Subject: [PATCH 01/10] Initial plan From a1883ec9e772f17490f14dd808a47d7be782b980 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 18:40:53 +0000 Subject: [PATCH 02/10] fix: remove scalar write support from ImageWriter and CellMapDatasetWriter Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com> --- src/cellmap_data/dataset_writer.py | 4 +--- src/cellmap_data/image_writer.py | 7 ++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cellmap_data/dataset_writer.py b/src/cellmap_data/dataset_writer.py index 780dbfa..263bce5 100644 --- a/src/cellmap_data/dataset_writer.py +++ b/src/cellmap_data/dataset_writer.py @@ -252,9 +252,7 @@ def __setitem__( for key, val in arrays.items(): if key in _SKIP_KEYS: continue - if isinstance(val, (int, float)): - item[key] = val - elif isinstance(val, dict): + if isinstance(val, dict): item[key] = {k: v[batch_i] for k, v in val.items()} else: item[key] = val[batch_i] diff --git a/src/cellmap_data/image_writer.py b/src/cellmap_data/image_writer.py index f699724..e8e5b43 100644 --- a/src/cellmap_data/image_writer.py +++ b/src/cellmap_data/image_writer.py @@ -170,7 +170,7 @@ def _zarr_array(self) -> zarr.Array: def __setitem__( self, coords: Mapping[str, float] | Mapping[str, Sequence], - data: torch.Tensor | ArrayLike | float | int, + data: torch.Tensor | ArrayLike, ) -> None: """Write *data* at the location given by *coords*. @@ -178,6 +178,11 @@ def __setitem__( - ``{axis: float}`` centre coordinates — single patch. - ``{axis: Sequence[float]}`` centres — batch. """ + if np.isscalar(data): + raise TypeError( + "Scalar writes are not supported. " + "Pass an array or tensor with shape matching the patch." + ) first = next(iter(coords.values())) if isinstance(first, (int, float)): self._write_single(coords, data) # type: ignore[arg-type] From 3ec2dab23f5894bd1adff8d8cd98503521b707cd Mon Sep 17 00:00:00 2001 From: Jeff Rhoades <37990507+rhoadesScholar@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:14:41 -0400 Subject: [PATCH 03/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/cellmap_data/image_writer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cellmap_data/image_writer.py b/src/cellmap_data/image_writer.py index a5a6b8d..e59f593 100644 --- a/src/cellmap_data/image_writer.py +++ b/src/cellmap_data/image_writer.py @@ -181,7 +181,10 @@ def __setitem__( Raises ------ TypeError - If *data* is a scalar (int or float). Use an array or tensor instead. + If *data* is a scalar (i.e. ``np.isscalar(data)`` is ``True``, including + Python and NumPy scalar types). Use a non-scalar array or tensor with + shape matching the patch instead. Zero-dimensional arrays/tensors are + also not supported for writes. """ if np.isscalar(data): raise TypeError( From 99e0dd7d8b510777c1504de75aa992a6f775b325 Mon Sep 17 00:00:00 2001 From: Jeff Rhoades <37990507+rhoadesScholar@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:15:39 -0400 Subject: [PATCH 04/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/cellmap_data/image_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cellmap_data/image_writer.py b/src/cellmap_data/image_writer.py index e59f593..82e7afb 100644 --- a/src/cellmap_data/image_writer.py +++ b/src/cellmap_data/image_writer.py @@ -226,7 +226,7 @@ def _write_single( if data_np.ndim == 0: raise TypeError( "Scalar writes are not supported. " - "Provide an array or tensor with the patch shape instead." + "Pass an array or tensor with shape matching the patch." ) data_np = data_np.astype(self.dtype) From 7554d4c19932d28f946454731c1958a25f696279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:22:50 +0000 Subject: [PATCH 05/10] test: add test for scalar-in-batch TypeError and explicit guard in dataset_writer Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com> --- src/cellmap_data/dataset_writer.py | 5 +++++ tests/test_writer.py | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/cellmap_data/dataset_writer.py b/src/cellmap_data/dataset_writer.py index 263bce5..271c493 100644 --- a/src/cellmap_data/dataset_writer.py +++ b/src/cellmap_data/dataset_writer.py @@ -252,6 +252,11 @@ def __setitem__( for key, val in arrays.items(): if key in _SKIP_KEYS: continue + if np.isscalar(val): + raise TypeError( + f"Scalar writes are not supported (key={key!r}). " + "Pass an array or tensor with a leading batch dimension." + ) if isinstance(val, dict): item[key] = {k: v[batch_i] for k, v in val.items()} else: diff --git a/tests/test_writer.py b/tests/test_writer.py index 9395197..27d9568 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -111,6 +111,18 @@ def test_setitem_batch(self, tmp_path): output = {"mito": torch.zeros(2, 4, 4, 4)} writer[idx_tensor] = output # should not raise + def test_setitem_batch_scalar_raises(self, tmp_path): + """Passing a scalar value in a batch write must raise TypeError.""" + import pytest + + writer = self._make_writer(tmp_path) + indices = writer.writer_indices[:2] + idx_tensor = torch.tensor(indices) + # Scalar instead of a batched array — should raise + output = {"mito": 1.0} + with pytest.raises(TypeError, match="Scalar writes are not supported"): + writer[idx_tensor] = output + def test_loader_iterable(self, tmp_path): writer = self._make_writer(tmp_path) loader = writer.loader(batch_size=2) From d26ace84411742f172254b44f22d4b34b235d798 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Fri, 13 Mar 2026 15:27:38 -0400 Subject: [PATCH 06/10] fix: add type check for written values in CellMapDatasetWriter --- src/cellmap_data/dataset_writer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cellmap_data/dataset_writer.py b/src/cellmap_data/dataset_writer.py index 271c493..48129c7 100644 --- a/src/cellmap_data/dataset_writer.py +++ b/src/cellmap_data/dataset_writer.py @@ -259,8 +259,12 @@ def __setitem__( ) if isinstance(val, dict): item[key] = {k: v[batch_i] for k, v in val.items()} - else: + elif hasattr(val, "__getitem__") and not isinstance(val, str): item[key] = val[batch_i] + else: + raise TypeError( + "Written values should be ArrayLike, or dictionaries of ArrayLike objects." + ) self.__setitem__(int(i), item) return From 8d446ccf56559040a610122071f212d658650cff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:42:08 +0000 Subject: [PATCH 07/10] refactor: apply rhoadesScholar suggestion - drop else branch, keep np.isscalar + hasattr guards Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com> --- src/cellmap_data/dataset_writer.py | 4 ---- tests/test_writer.py | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/cellmap_data/dataset_writer.py b/src/cellmap_data/dataset_writer.py index 48129c7..f1d11cb 100644 --- a/src/cellmap_data/dataset_writer.py +++ b/src/cellmap_data/dataset_writer.py @@ -261,10 +261,6 @@ def __setitem__( item[key] = {k: v[batch_i] for k, v in val.items()} elif hasattr(val, "__getitem__") and not isinstance(val, str): item[key] = val[batch_i] - else: - raise TypeError( - "Written values should be ArrayLike, or dictionaries of ArrayLike objects." - ) self.__setitem__(int(i), item) return diff --git a/tests/test_writer.py b/tests/test_writer.py index 27d9568..de96ed3 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -3,6 +3,7 @@ from __future__ import annotations import numpy as np +import pytest import torch from cellmap_data import CellMapDatasetWriter @@ -113,8 +114,6 @@ def test_setitem_batch(self, tmp_path): def test_setitem_batch_scalar_raises(self, tmp_path): """Passing a scalar value in a batch write must raise TypeError.""" - import pytest - writer = self._make_writer(tmp_path) indices = writer.writer_indices[:2] idx_tensor = torch.tensor(indices) From 02f8b6babc73ede286c88ae7eb06f184837158b3 Mon Sep 17 00:00:00 2001 From: Jeff Rhoades <37990507+rhoadesScholar@users.noreply.github.com> Date: Wed, 18 Mar 2026 13:44:59 -0400 Subject: [PATCH 08/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/cellmap_data/dataset_writer.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cellmap_data/dataset_writer.py b/src/cellmap_data/dataset_writer.py index f1d11cb..6a35f60 100644 --- a/src/cellmap_data/dataset_writer.py +++ b/src/cellmap_data/dataset_writer.py @@ -261,6 +261,13 @@ def __setitem__( item[key] = {k: v[batch_i] for k, v in val.items()} elif hasattr(val, "__getitem__") and not isinstance(val, str): item[key] = val[batch_i] + else: + raise TypeError( + "Unsupported batched value type for key " + f"{key!r}: {type(val).__name__}. Expected a dict of " + "batch-indexable values or a batch-indexable " + "array/tensor/sequence." + ) self.__setitem__(int(i), item) return From c6d1338cdb636303e4ef1c31f61618e68535c106 Mon Sep 17 00:00:00 2001 From: Jeff Rhoades <37990507+rhoadesScholar@users.noreply.github.com> Date: Wed, 18 Mar 2026 13:45:43 -0400 Subject: [PATCH 09/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/cellmap_data/image_writer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cellmap_data/image_writer.py b/src/cellmap_data/image_writer.py index 82e7afb..7efb0d2 100644 --- a/src/cellmap_data/image_writer.py +++ b/src/cellmap_data/image_writer.py @@ -191,6 +191,18 @@ def __setitem__( "Scalar writes are not supported. " "Pass an array or tensor with shape matching the patch." ) + # Explicitly reject zero-dimensional arrays/tensors, which are not caught + # by np.isscalar and are documented as unsupported for writes. + if isinstance(data, np.ndarray) and data.ndim == 0: + raise TypeError( + "Zero-dimensional NumPy arrays are not supported for writes. " + "Pass a non-scalar array or tensor with shape matching the patch." + ) + if torch.is_tensor(data) and data.dim() == 0: + raise TypeError( + "Zero-dimensional torch.Tensors are not supported for writes. " + "Pass a non-scalar tensor or array with shape matching the patch." + ) first = next(iter(coords.values())) if isinstance(first, (int, float)): self._write_single(coords, data) # type: ignore[arg-type] From c7141ced9e7e3bb1ee2ac0f47a77b5524c69ba4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Mar 2026 18:05:34 +0000 Subject: [PATCH 10/10] test: add coverage for 0-D array/tensor rejection and unsupported batch type Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com> --- tests/test_writer.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_writer.py b/tests/test_writer.py index de96ed3..5fca73b 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -57,6 +57,30 @@ def test_repr(self, tmp_path): ) assert "ImageWriter" in repr(writer) + def _make_image_writer(self, tmp_path): + return ImageWriter( + path=str(tmp_path / "out.zarr" / "mito"), + target_class="mito", + scale={"z": 8.0, "y": 8.0, "x": 8.0}, + bounding_box={"z": (0.0, 128.0), "y": (0.0, 128.0), "x": (0.0, 128.0)}, + write_voxel_shape={"z": 4, "y": 4, "x": 4}, + overwrite=True, + ) + + def test_setitem_zero_dim_ndarray_raises(self, tmp_path): + """A 0-D NumPy array must raise TypeError with a clear message.""" + writer = self._make_image_writer(tmp_path) + center = {"z": 16.0, "y": 16.0, "x": 16.0} + with pytest.raises(TypeError, match="Zero-dimensional NumPy arrays"): + writer[center] = np.array(1.0) + + def test_setitem_zero_dim_tensor_raises(self, tmp_path): + """A 0-D torch.Tensor must raise TypeError with a clear message.""" + writer = self._make_image_writer(tmp_path) + center = {"z": 16.0, "y": 16.0, "x": 16.0} + with pytest.raises(TypeError, match="Zero-dimensional torch.Tensors"): + writer[center] = torch.tensor(1.0) + class TestCellMapDatasetWriter: def _make_writer(self, tmp_path): @@ -122,6 +146,14 @@ def test_setitem_batch_scalar_raises(self, tmp_path): with pytest.raises(TypeError, match="Scalar writes are not supported"): writer[idx_tensor] = output + def test_setitem_batch_unsupported_type_raises(self, tmp_path): + """A non-dict, non-indexable value in a batch write must raise TypeError.""" + writer = self._make_writer(tmp_path) + indices = writer.writer_indices[:2] + idx_tensor = torch.tensor(indices) + with pytest.raises(TypeError, match="Unsupported batched value type"): + writer[idx_tensor] = {"mito": object()} + def test_loader_iterable(self, tmp_path): writer = self._make_writer(tmp_path) loader = writer.loader(batch_size=2)