From 767b64235f26086cb1cdfdcd2d1a486a8a43621e Mon Sep 17 00:00:00 2001 From: William Moore Date: Thu, 6 Jun 2024 13:51:12 +0100 Subject: [PATCH] Fix format.matches for v0.5 --- ome_zarr/format.py | 21 ++++++++++++++++++++- ome_zarr/io.py | 8 +++----- ome_zarr/reader.py | 10 +++++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/ome_zarr/format.py b/ome_zarr/format.py index 8d4238b0..2f0acc67 100644 --- a/ome_zarr/format.py +++ b/ome_zarr/format.py @@ -8,6 +8,8 @@ LOGGER = logging.getLogger("ome_zarr.format") +NGFF_URL_0_5 = "https://ngff.openmicroscopy.org/0.5" + def format_from_version(version: str) -> "Format": for fmt in format_implementations(): @@ -24,6 +26,7 @@ def format_implementations() -> Iterator["Format"]: """ Return an instance of each format implementation, newest to oldest. """ + yield FormatV05() yield FormatV04() yield FormatV03() yield FormatV02() @@ -353,7 +356,7 @@ def version(self) -> str: @property def version_key(self) -> str: - return "https://ngff.openmicroscopy.org/0.5" + return NGFF_URL_0_5 def init_store(self, path: str, mode: str = "r") -> FSStore: """ @@ -376,5 +379,21 @@ def init_store(self, path: str, mode: str = "r") -> FSStore: print("Created v0.5 store %s(%s, %s, %s)" % (cls, path, mode, kwargs)) return store + def matches(self, metadata: dict) -> bool: + """Version 0.5+ defined by version_key (URL)""" + version = self._get_metadata_version(metadata) + LOGGER.debug("%s matches %s?", self.version, version) + return version == self.version_key + + def _get_metadata_version(self, metadata: dict) -> Optional[str]: + """ + For version 0.5+ we use the NGFF_URL_0_5 key + + Returns the version of the first object found in the metadata, + checking for 'multiscales', 'plate', 'well' etc + """ + if self.version_key in metadata.get("attributes", {}): + return self.version_key + CurrentFormat = FormatV05 diff --git a/ome_zarr/io.py b/ome_zarr/io.py index f9e81c8d..0c7e56a7 100644 --- a/ome_zarr/io.py +++ b/ome_zarr/io.py @@ -71,7 +71,7 @@ def __init__( # loader = CurrentFormat() self.__init_metadata() - print("init self.__metadata", self.__metadata) + print("ZarrLocation init self.__metadata", self.__metadata) detected = detect_format(self.__metadata, loader) LOGGER.debug("ZarrLocation.__init__ %s detected: %s", self.__path, detected) if detected != self.__fmt: @@ -110,20 +110,18 @@ def __init_metadata(self) -> None: # NB: zarr_format not supported in Group.open() or Array.open() yet # We want to use zarr_format=None to handle v2 or v3 zarr_group = Group.open(self.__store) #, zarr_format=None) - self.zgroup: JSONDict = zarr_group.metadata.to_dict() + self.zgroup = zarr_group.metadata.to_dict() self.__metadata = self.zgroup except FileNotFoundError: # group doesn't exist yet, try array try: zarr_array = Array.open(self.__store) #, zarr_format=None) - self.zarray: JSONDict = zarr_array.metadata.to_dict() + self.zarray = zarr_array.metadata.to_dict() self.__metadata = self.zarray except (ValueError, KeyError, FileNotFoundError): # exceptions raised may change here? - print("__init_metadata DOESN'T EXIST", self.__store) self.__exists = False - print("__init_metadat EXISTS?", self.__exists, self.__metadata) # self.zarray: JSONDict = await self.get_json(".zarray") # self.zgroup: JSONDict = await self.get_json(".zgroup") # v3_json = await self.get_json("zarr.json") diff --git a/ome_zarr/reader.py b/ome_zarr/reader.py index 55f84ec0..4ace8237 100644 --- a/ome_zarr/reader.py +++ b/ome_zarr/reader.py @@ -9,7 +9,7 @@ import numpy as np from .axes import Axes -from .format import format_from_version +from .format import format_from_version, NGFF_URL_0_5 from .io import ZarrLocation from .types import JSONDict @@ -176,6 +176,11 @@ def __init__(self, node: Node) -> None: LOGGER.debug(v) def lookup(self, key: str, default: Any) -> Any: + + # Handle zarr V3 where everything is under "attributes" + if NGFF_URL_0_5 in self.zarr.root_attrs.get("attributes", {}): + return self.zarr.root_attrs.get("attributes", {}).get(NGFF_URL_0_5, {}).get(key, default) + return self.zarr.root_attrs.get(key, default) @@ -270,9 +275,12 @@ class Multiscales(Spec): @staticmethod def matches(zarr: ZarrLocation) -> bool: """is multiscales metadata present?""" + if zarr.zgroup: if "multiscales" in zarr.root_attrs: return True + if "multiscales" in (zarr.root_attrs.get("attributes", {}).get(NGFF_URL_0_5, {})): + return True return False def __init__(self, node: Node) -> None: