From e5219fc91ad1f32125af0e2ef154a61fbd923590 Mon Sep 17 00:00:00 2001 From: parallelArchitect <236280545+parallelArchitect@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:23:16 -0400 Subject: [PATCH 1/2] fix: add zarr3 to ImageFormat literal COSEM API now returns format: zarr3 for some images, which the pydantic schema rejected as an invalid literal. Hit this while fetching jrc_hela-3 / er-mem_pred to validate #124 against real data. --- src/microsim/cosem/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/microsim/cosem/models.py b/src/microsim/cosem/models.py index e788c85..d8bf458 100644 --- a/src/microsim/cosem/models.py +++ b/src/microsim/cosem/models.py @@ -38,6 +38,7 @@ ImageFormat = Literal[ "n5", "zarr", + "zarr3", "precomputed", "neuroglancer_precomputed", "neuroglancer_multilod_draco", From 64de69d9e906cb3b7fc123ee3ffc87e0c535170e Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 21 Jun 2026 21:09:18 -0400 Subject: [PATCH 2/2] add full zarr3 fix and test --- src/microsim/cosem/_tstore.py | 1 + src/microsim/cosem/models.py | 13 +++++++++++-- tests/test_cosem.py | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/microsim/cosem/_tstore.py b/src/microsim/cosem/_tstore.py index 25af28e..ef49d6c 100644 --- a/src/microsim/cosem/_tstore.py +++ b/src/microsim/cosem/_tstore.py @@ -32,6 +32,7 @@ "precomputed": "neuroglancer_precomputed", "n5": "n5", "zarr": "zarr", + "zarr3": "zarr3", } diff --git a/src/microsim/cosem/models.py b/src/microsim/cosem/models.py index d8bf458..0563e46 100644 --- a/src/microsim/cosem/models.py +++ b/src/microsim/cosem/models.py @@ -104,8 +104,11 @@ def scales(self) -> list[str]: """Fetch all available scales for the image from s3.""" if not getattr(self, "_scales", None): scales = [] + # n5/zarr v2 multiscale lives at the top level, while zarr v3 + # (OME-NGFF 0.5) nests it under the "ome" namespace. + ome = self.attrs.get("ome", {}) # n5 multiscale - if multi := self.attrs.get("multiscales"): + if multi := (self.attrs.get("multiscales") or ome.get("multiscales")): for scale in multi: if dsets := scale.get("datasets"): for dset in dsets: @@ -185,10 +188,16 @@ def attrs(self) -> dict[str, Any]: attr = "/attributes.json" elif self.format == "zarr": attr = "/.zattrs" + elif self.format == "zarr3": + attr = "/zarr.json" elif self.format == "precomputed": attr = "/info" try: - self._attrs = json.load(fetch_s3(self.url + attr)) + data = json.load(fetch_s3(self.url + attr)) + # zarr v3 nests user attributes under an "attributes" namespace + if self.format == "zarr3": + data = data.get("attributes", {}) + self._attrs = data except Exception: self._attrs = {} return self._attrs # type: ignore [no-any-return] diff --git a/tests/test_cosem.py b/tests/test_cosem.py index b237433..a1e48fd 100644 --- a/tests/test_cosem.py +++ b/tests/test_cosem.py @@ -53,6 +53,20 @@ def test_cosem_image() -> None: img = dataset.image(name="not real") +@skipif_no_internet +def test_cosem_zarr3() -> None: + # COSEM now serves some images as zarr v3; make sure we can validate, + # resolve scales, and read them. See #140 / #141. + dataset = CosemDataset.fetch("jrc_mus-skel-muscle-1") + img = dataset.image(name="fibsem-uint8") + assert img.format == "zarr3" + assert img.scales # multiscale metadata nested under the "ome" namespace + # read a mid-pyramid level (all dims > 1) to exercise the zarr3 driver + store = img.read(level=8, bin_mode="standard") + assert isinstance(store, ts.TensorStore) + assert store.ndim == 3 + + @skipif_no_internet def test_cosem_view() -> None: orgs = organelles()