-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
not quite working zarr implementation
- Loading branch information
1 parent
a345cc6
commit d884055
Showing
8 changed files
with
297 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,121 @@ | ||
""" | ||
Interface to zarr arrays | ||
(Not Implemented) | ||
""" | ||
|
||
import contextlib | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
from typing import Any, Optional, Union, Sequence | ||
|
||
from numpydantic.interface.interface import Interface | ||
|
||
try: | ||
from zarr.core import Array as ZarrArray | ||
from zarr.storage import StoreLike | ||
import zarr | ||
except ImportError: | ||
ZarrArray = None | ||
StoreLike = None | ||
storage = None | ||
|
||
|
||
@dataclass | ||
class ZarrArrayPath: | ||
""" | ||
Map to an array within a zarr store. | ||
See :func:`zarr.open` | ||
""" | ||
|
||
file: Union[Path, str] | ||
"""Location of Zarr store file or directory""" | ||
path: Optional[str] = None | ||
"""Path to array within hierarchical zarr store""" | ||
|
||
def open(self, **kwargs) -> ZarrArray: | ||
return zarr.open(str(self.file), path=self.path, **kwargs) | ||
|
||
@classmethod | ||
def from_iterable(cls, spec: Sequence) -> "ZarrArrayPath": | ||
if len(spec) == 1: | ||
return ZarrArrayPath(file=spec[0]) | ||
elif len(spec) == 2: | ||
return ZarrArrayPath(file=spec[0], path=spec[1]) | ||
else: | ||
raise ValueError("Only len 1-2 iterables can be used for a ZarrArrayPath") | ||
|
||
|
||
class ZarrInterface(Interface): | ||
""" | ||
Interface to in-memory or on-disk zarr arrays | ||
""" | ||
|
||
input_types = (Path, ZarrArray, ZarrArrayPath) | ||
return_type = ZarrArray | ||
|
||
@classmethod | ||
def enabled(cls) -> bool: | ||
"""True if zarr is installed""" | ||
return ZarrArray is not None | ||
|
||
@staticmethod | ||
def _get_array( | ||
array: Union[ZarrArray, str, Path, ZarrArrayPath, Sequence] | ||
) -> ZarrArray: | ||
if isinstance(array, ZarrArray): | ||
return array | ||
|
||
if isinstance(array, (str, Path)): | ||
array = ZarrArrayPath(file=array) | ||
elif isinstance(array, (tuple, list)): | ||
array = ZarrArrayPath.from_iterable(array) | ||
|
||
return array.open(mode="a") | ||
|
||
@classmethod | ||
def check(cls, array: Any) -> bool: | ||
""" | ||
Check if array is in-memory zarr array, | ||
a path to a zarr array, or a :class:`.ZarrArrayPath` | ||
""" | ||
if isinstance(array, ZarrArray): | ||
return True | ||
|
||
# See if can be coerced to ZarrArrayPath | ||
if isinstance(array, (Path, str)): | ||
array = ZarrArrayPath(file=array) | ||
|
||
if isinstance(array, (tuple, list)): | ||
# something that can be coerced to ZarrArrayPath | ||
with contextlib.suppress(ValueError): | ||
array = ZarrArrayPath.from_iterable(array) | ||
|
||
if isinstance(array, ZarrArrayPath): | ||
with contextlib.suppress(Exception): | ||
arr = array.open(mode="r") | ||
if isinstance(arr, ZarrArray): | ||
return True | ||
|
||
return False | ||
|
||
def before_validation( | ||
self, array: Union[ZarrArray, str, Path, ZarrArrayPath, Sequence] | ||
) -> ZarrArray: | ||
""" | ||
Ensure that the zarr array is opened | ||
""" | ||
return self._get_array(array) | ||
|
||
@classmethod | ||
def to_json( | ||
cls, array: Union[ZarrArray, str, Path, ZarrArrayPath, Sequence] | ||
) -> dict: | ||
""" | ||
Dump just the metadata for an array from :meth:`zarr.core.Array.info_items` | ||
plus the :meth:`zarr.core.Array.hexdigest` | ||
""" | ||
array = cls._get_array(array) | ||
info = array.info_items() | ||
info_dict = {i[0]: i[1] for i in info} | ||
info_dict["hexdigest"] = array.hexdigest() | ||
return info_dict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import pytest | ||
import zarr | ||
|
||
from pydantic import ValidationError | ||
|
||
from numpydantic.interface import ZarrInterface | ||
|
||
|
||
@pytest.fixture() | ||
def dir_array(tmp_output_dir_func) -> zarr.DirectoryStore: | ||
store = zarr.DirectoryStore(tmp_output_dir_func / "array.zarr") | ||
return store | ||
|
||
|
||
@pytest.fixture() | ||
def zip_array(tmp_output_dir_func) -> zarr.ZipStore: | ||
store = zarr.ZipStore(tmp_output_dir_func / "array.zip", mode="w") | ||
return store | ||
|
||
|
||
@pytest.fixture() | ||
def nested_dir_array(tmp_output_dir_func) -> zarr.NestedDirectoryStore: | ||
store = zarr.NestedDirectoryStore(tmp_output_dir_func / "nested") | ||
return store | ||
|
||
|
||
STORES = ( | ||
dir_array, | ||
zip_array, | ||
) | ||
"""stores for single arrays""" | ||
|
||
|
||
def test_zarr_enabled(): | ||
assert ZarrInterface.enabled() | ||
|
||
|
||
def test_zarr_check(interface_type): | ||
""" | ||
We should only use the zarr interface for zarr-like things | ||
""" | ||
if interface_type[1] is ZarrInterface: | ||
assert ZarrInterface.check(interface_type[0]) | ||
else: | ||
assert not ZarrInterface.check(interface_type[0]) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"array,passes", | ||
[ | ||
(zarr.zeros((5, 10)), True), | ||
(zarr.zeros((5, 10, 3)), True), | ||
(zarr.zeros((5, 10, 3, 4)), True), | ||
(zarr.zeros((5, 10, 4)), False), | ||
(zarr.zeros((5, 10, 3, 6)), False), | ||
(zarr.zeros((5, 10, 4, 6)), False), | ||
], | ||
) | ||
def test_zarr_shape(model_rgb, array, passes): | ||
if passes: | ||
model_rgb(array=array) | ||
else: | ||
with pytest.raises(ValidationError): | ||
model_rgb(array=array) |