-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zarr v3 #404
base: master
Are you sure you want to change the base?
Zarr v3 #404
Changes from all commits
b0d6b3d
da8c32f
19b89a8
a954161
80f6e01
e568911
b49ecc8
18abe02
86142c3
31584bf
daa3546
fa29ccc
8fc02b4
29890b8
35bc979
75ba690
52aceb0
55d4ba9
0ea21bc
7fc113b
94f7ace
d140c6d
f7b5f98
c527c77
d8d5378
2138160
af2648d
1ea9e1a
7754774
499531f
c0fe50d
e021c13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,14 +3,14 @@ | |
Primary entry point is the :func:`~ome_zarr.io.parse_url` method. | ||
""" | ||
|
||
import json | ||
import logging | ||
from pathlib import Path | ||
from typing import Optional, Union | ||
from urllib.parse import urljoin | ||
|
||
import dask.array as da | ||
from zarr.storage import FSStore | ||
import zarr | ||
from zarr.storage import LocalStore, RemoteStore, StoreLike | ||
|
||
from .format import CurrentFormat, Format, detect_format | ||
from .types import JSONDict | ||
|
@@ -20,7 +20,7 @@ | |
|
||
class ZarrLocation: | ||
""" | ||
IO primitive for reading and writing Zarr data. Uses FSStore for all | ||
IO primitive for reading and writing Zarr data. Uses a store for all | ||
data access. | ||
No assumptions about the existence of the given path string are made. | ||
|
@@ -29,7 +29,7 @@ class ZarrLocation: | |
|
||
def __init__( | ||
self, | ||
path: Union[Path, str, FSStore], | ||
path: StoreLike, | ||
mode: str = "r", | ||
fmt: Format = CurrentFormat(), | ||
) -> None: | ||
|
@@ -40,18 +40,21 @@ def __init__( | |
self.__path = str(path.resolve()) | ||
elif isinstance(path, str): | ||
self.__path = path | ||
elif isinstance(path, FSStore): | ||
elif isinstance(path, RemoteStore): | ||
self.__path = path.path | ||
elif isinstance(path, LocalStore): | ||
self.__path = str(path.root) | ||
else: | ||
raise TypeError(f"not expecting: {type(path)}") | ||
|
||
loader = fmt | ||
if loader is None: | ||
loader = CurrentFormat() | ||
self.__store: FSStore = ( | ||
path if isinstance(path, FSStore) else loader.init_store(self.__path, mode) | ||
self.__store: RemoteStore = ( | ||
path | ||
if isinstance(path, RemoteStore) | ||
else loader.init_store(self.__path, mode) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would also checking for LocalStore here work? |
||
) | ||
|
||
self.__init_metadata() | ||
detected = detect_format(self.__metadata, loader) | ||
LOGGER.debug("ZarrLocation.__init__ %s detected: %s", path, detected) | ||
|
@@ -67,16 +70,41 @@ def __init_metadata(self) -> None: | |
""" | ||
Load the Zarr metadata files for the given location. | ||
""" | ||
self.zarray: JSONDict = self.get_json(".zarray") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can |
||
self.zgroup: JSONDict = self.get_json(".zgroup") | ||
self.zgroup: JSONDict = {} | ||
self.zarray: JSONDict = {} | ||
self.__metadata: JSONDict = {} | ||
self.__exists: bool = True | ||
if self.zgroup: | ||
self.__metadata = self.get_json(".zattrs") | ||
elif self.zarray: | ||
self.__metadata = self.get_json(".zattrs") | ||
else: | ||
self.__exists = False | ||
# If we want to *create* a new zarr v2 group, we need to specify | ||
# zarr_format. This is not needed for reading. | ||
zarr_format = None | ||
if self.__mode == "w": | ||
# For now, let's support writing of zarr v2 | ||
# TODO: handle writing of zarr v2 OR zarr v3 | ||
zarr_format = 2 | ||
try: | ||
group = zarr.open_group( | ||
store=self.__store, path="/", mode=self.__mode, zarr_format=zarr_format | ||
) | ||
self.zgroup = group.attrs.asdict() | ||
# For zarr v3, everything is under the "ome" namespace | ||
if "ome" in self.zgroup: | ||
self.zgroup = self.zgroup["ome"] | ||
self.__metadata = self.zgroup | ||
except (ValueError, FileNotFoundError): | ||
try: | ||
array = zarr.open_array( | ||
store=self.__store, | ||
path="/", | ||
mode=self.__mode, | ||
zarr_format=zarr_format, | ||
) | ||
self.zarray = array.attrs.asdict() | ||
self.__metadata = self.zarray | ||
except (ValueError, FileNotFoundError): | ||
# We actually get a ValueError when the file is not found | ||
# /zarr-python/src/zarr/abc/store.py", line 189, in _check_writable | ||
# raise ValueError("store mode does not support writing") | ||
self.__exists = False | ||
|
||
def __repr__(self) -> str: | ||
"""Print the path as well as whether this is a group or an array.""" | ||
|
@@ -104,7 +132,7 @@ def path(self) -> str: | |
return self.__path | ||
|
||
@property | ||
def store(self) -> FSStore: | ||
def store(self) -> RemoteStore: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unsure why this isn't also |
||
"""Return the initialized store for this location""" | ||
assert self.__store is not None | ||
return self.__store | ||
|
@@ -154,11 +182,9 @@ def get_json(self, subpath: str) -> JSONDict: | |
All other exceptions log at the ERROR level. | ||
""" | ||
try: | ||
data = self.__store.get(subpath) | ||
if not data: | ||
return {} | ||
return json.loads(data) | ||
except KeyError: | ||
array_or_group = zarr.open_group(store=self.__store, path="/") | ||
return array_or_group.attrs.asdict() | ||
except (KeyError, FileNotFoundError): | ||
LOGGER.debug("JSON not found: %s", subpath) | ||
return {} | ||
except Exception: | ||
|
@@ -193,10 +219,11 @@ def _isfile(self) -> bool: | |
Return whether the current underlying implementation | ||
points to a local file or not. | ||
""" | ||
return self.__store.fs.protocol == "file" or self.__store.fs.protocol == ( | ||
"file", | ||
"local", | ||
) | ||
# return self.__store.fs.protocol == "file" or self.__store.fs.protocol == ( | ||
# "file", | ||
# "local", | ||
# ) | ||
return isinstance(self.__store, LocalStore) | ||
|
||
def _ishttp(self) -> bool: | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be
| LocalStore
as well?