ezarr 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ezarr-1.0.0/.gitignore +2 -0
- ezarr-1.0.0/.pre-commit-config.yaml +17 -0
- ezarr-1.0.0/.python-version +1 -0
- ezarr-1.0.0/PKG-INFO +132 -0
- ezarr-1.0.0/README.md +123 -0
- ezarr-1.0.0/ezarr/__init__.py +5 -0
- ezarr-1.0.0/ezarr/_repr.py +84 -0
- ezarr-1.0.0/ezarr/dict.py +264 -0
- ezarr-1.0.0/ezarr/io.py +95 -0
- ezarr-1.0.0/ezarr/list.py +369 -0
- ezarr-1.0.0/ezarr/names.py +26 -0
- ezarr-1.0.0/ezarr/object.py +53 -0
- ezarr-1.0.0/ezarr/py.typed +0 -0
- ezarr-1.0.0/ezarr/types.py +147 -0
- ezarr-1.0.0/pyproject.toml +40 -0
- ezarr-1.0.0/tests/conftest.py +13 -0
- ezarr-1.0.0/tests/test_dict.py +166 -0
- ezarr-1.0.0/tests/test_list.py +120 -0
- ezarr-1.0.0/uv.lock +504 -0
ezarr-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fail_fast: true
|
|
2
|
+
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
5
|
+
# Ruff version.
|
|
6
|
+
rev: v0.11.13
|
|
7
|
+
hooks:
|
|
8
|
+
# Run the linter.
|
|
9
|
+
- id: ruff-check
|
|
10
|
+
name: ruff fix
|
|
11
|
+
args: [ --fix, --output-format, concise ]
|
|
12
|
+
# Fix old typing and syntax
|
|
13
|
+
- id: ruff-check
|
|
14
|
+
name: ruff update
|
|
15
|
+
args: [ --fix, --select, UP,F401, --output-format, concise ]
|
|
16
|
+
# Run the formatter.
|
|
17
|
+
- id: ruff-format
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
ezarr-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ezarr
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Tools for easily working with Zarr groups and arrays
|
|
5
|
+
Author-email: Matteo Bouvier <matteo.bouvier@lyon.unicancer.fr>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: zarr>=3
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# eZarr
|
|
11
|
+
|
|
12
|
+
Easily store any Python object inside [zarr](https://zarr.readthedocs.io/en/latest/) hierarchies.
|
|
13
|
+
This package defines proxies for regular Python lists (EZList) and dicts (EZDict), built as wrappers around zarr Arrays and Groups.
|
|
14
|
+
It also defines a Protocol for writing and loading any object in zarr hierarchies.
|
|
15
|
+
|
|
16
|
+
## EZList
|
|
17
|
+
An EZlist is a proxy for a Python list built on top of a zarr.Array.
|
|
18
|
+
It allows storing mixed arbitrary objects in a 1 dimensional sequence.
|
|
19
|
+
|
|
20
|
+
### Examples
|
|
21
|
+
```python
|
|
22
|
+
>>> from ezarr import EZList
|
|
23
|
+
>>> from pathlib import Path
|
|
24
|
+
|
|
25
|
+
# EZList creation from a zarr.Array:
|
|
26
|
+
>>> EZList(zarr.array([1, 2, 3]))
|
|
27
|
+
EZList[1, 2, 3]
|
|
28
|
+
|
|
29
|
+
# EZList creation from a list:
|
|
30
|
+
>>> data = [1, "some text", Path("some/path.txt")]
|
|
31
|
+
>>> ez_list = EZList.from_list(data, store={}, name="data")
|
|
32
|
+
# ~~ ~~~~ a name for the list inside the store
|
|
33
|
+
# |
|
|
34
|
+
# or any zarr StoreLike object to store the list
|
|
35
|
+
|
|
36
|
+
>>> ez_list
|
|
37
|
+
EZList[1, 'some text', PosixPath('some/path.txt')]
|
|
38
|
+
|
|
39
|
+
# EZLists support general list methods:
|
|
40
|
+
>>> ez_list[0]
|
|
41
|
+
1
|
|
42
|
+
|
|
43
|
+
>>> del ez_list[1]
|
|
44
|
+
>>> ez_list
|
|
45
|
+
EZList[1, PosixPath('some/path.txt')]
|
|
46
|
+
|
|
47
|
+
>>> ez_list.append(3.14)
|
|
48
|
+
>>> ez_list
|
|
49
|
+
EZList[1, PosixPath('some/path.txt'), 3.14]
|
|
50
|
+
|
|
51
|
+
>>> ez_list.insert(0, [1, 2, 3])
|
|
52
|
+
>>> ez_list
|
|
53
|
+
EZList[[1, 2, 3], 1, PosixPath('/some/path.txt'), 3.14]
|
|
54
|
+
|
|
55
|
+
# EZList save to zarr store:
|
|
56
|
+
>>> ez_list.save("path/to/store", name="saved_list")
|
|
57
|
+
|
|
58
|
+
# EZList load from zarr store:
|
|
59
|
+
>>> EZList.open("path/to/store", name="saved_list")
|
|
60
|
+
EZList[[1, 2, 3], 1, PosixPath('/some/path.txt'), 3.14]
|
|
61
|
+
|
|
62
|
+
# EZList conversion to a regular list:
|
|
63
|
+
>>> ez_list.copy()
|
|
64
|
+
[[1, 2, 3], 1, PosixPath('some/path.txt'), 3.14]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## EZDict
|
|
68
|
+
An EZDict is a proxy for a Python dict built on top of a zarr.Group.
|
|
69
|
+
It expands zarr.Groups to store and retrieve arbitrary objects.
|
|
70
|
+
|
|
71
|
+
### Examples
|
|
72
|
+
```python
|
|
73
|
+
>>> from ezarr import EZDict
|
|
74
|
+
|
|
75
|
+
# EZdDct creation for a zarr.Group:
|
|
76
|
+
>>> EZDict(zarr.open_group({}))
|
|
77
|
+
EZDict{}
|
|
78
|
+
|
|
79
|
+
# EZDict creation for a dict:
|
|
80
|
+
>>> data = {"a": 1, "b": [1, 2, 3], "c": {"d": "some text"}}
|
|
81
|
+
>>> ez_dict = EZDict.from_dict(data, store={})
|
|
82
|
+
# ~~ or any zarr StoreLike object to store the dict into # |
|
|
83
|
+
|
|
84
|
+
# EZDict lazy representation:
|
|
85
|
+
>>> ez_dict
|
|
86
|
+
EZDict{
|
|
87
|
+
a: 1,
|
|
88
|
+
b: [1 2 3],
|
|
89
|
+
c: {...} # <-- note the nested dict was not loaded for the representation
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# EZDict save to zarr store:
|
|
93
|
+
>>> ez_dict.save("path/to/store", name="saved_dict")
|
|
94
|
+
|
|
95
|
+
# EZDict load from zarr store:
|
|
96
|
+
>>> EZDict.open("path/to/store", name="saved_dict")
|
|
97
|
+
EZDict{
|
|
98
|
+
a: 1,
|
|
99
|
+
b: [1 2 3],
|
|
100
|
+
c: {...}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# EZDict conversion to a regular dict:
|
|
104
|
+
>>> ez_dict.copy()
|
|
105
|
+
{'a': np.int64(1), 'b': array([1, 2, 3]), 'c': {'d': np.str_('some text')}}
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## SupportsEZReadWrite
|
|
110
|
+
The `SupportsEZReadWrite` protocol, once implemented on an object, allows it to be store in and retrieved from a zarr hierarchy.
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
@runtime_checkable
|
|
114
|
+
class SupportsEZRead(Protocol):
|
|
115
|
+
@classmethod
|
|
116
|
+
def __ez_read__(cls, grp: zarr.Group) -> Self: ...
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@runtime_checkable
|
|
120
|
+
class SupportsEZWrite(Protocol):
|
|
121
|
+
def __ez_write__(self, grp: zarr.Group) -> None: ...
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@runtime_checkable
|
|
125
|
+
class SupportsEZReadWrite(SupportsEZRead, SupportsEZWrite, Protocol): ...
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This protocol has 2 parts:
|
|
129
|
+
- `SupportsEZRead` defines the classmethod `__ez_read__(cls, grp: zarr.Group) -> Self` for reconstructing an object from a zarr.Group.
|
|
130
|
+
- `SupportsEZWrite` defines the method `__ez_write__(self, grp: zarr.Group) -> None` for storing an object in a zarr.Group.
|
|
131
|
+
|
|
132
|
+
Once implemented on an object, this protocol allows that object to be automatically saved and loaded in EZLists and EZDicts.
|
ezarr-1.0.0/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# eZarr
|
|
2
|
+
|
|
3
|
+
Easily store any Python object inside [zarr](https://zarr.readthedocs.io/en/latest/) hierarchies.
|
|
4
|
+
This package defines proxies for regular Python lists (EZList) and dicts (EZDict), built as wrappers around zarr Arrays and Groups.
|
|
5
|
+
It also defines a Protocol for writing and loading any object in zarr hierarchies.
|
|
6
|
+
|
|
7
|
+
## EZList
|
|
8
|
+
An EZlist is a proxy for a Python list built on top of a zarr.Array.
|
|
9
|
+
It allows storing mixed arbitrary objects in a 1 dimensional sequence.
|
|
10
|
+
|
|
11
|
+
### Examples
|
|
12
|
+
```python
|
|
13
|
+
>>> from ezarr import EZList
|
|
14
|
+
>>> from pathlib import Path
|
|
15
|
+
|
|
16
|
+
# EZList creation from a zarr.Array:
|
|
17
|
+
>>> EZList(zarr.array([1, 2, 3]))
|
|
18
|
+
EZList[1, 2, 3]
|
|
19
|
+
|
|
20
|
+
# EZList creation from a list:
|
|
21
|
+
>>> data = [1, "some text", Path("some/path.txt")]
|
|
22
|
+
>>> ez_list = EZList.from_list(data, store={}, name="data")
|
|
23
|
+
# ~~ ~~~~ a name for the list inside the store
|
|
24
|
+
# |
|
|
25
|
+
# or any zarr StoreLike object to store the list
|
|
26
|
+
|
|
27
|
+
>>> ez_list
|
|
28
|
+
EZList[1, 'some text', PosixPath('some/path.txt')]
|
|
29
|
+
|
|
30
|
+
# EZLists support general list methods:
|
|
31
|
+
>>> ez_list[0]
|
|
32
|
+
1
|
|
33
|
+
|
|
34
|
+
>>> del ez_list[1]
|
|
35
|
+
>>> ez_list
|
|
36
|
+
EZList[1, PosixPath('some/path.txt')]
|
|
37
|
+
|
|
38
|
+
>>> ez_list.append(3.14)
|
|
39
|
+
>>> ez_list
|
|
40
|
+
EZList[1, PosixPath('some/path.txt'), 3.14]
|
|
41
|
+
|
|
42
|
+
>>> ez_list.insert(0, [1, 2, 3])
|
|
43
|
+
>>> ez_list
|
|
44
|
+
EZList[[1, 2, 3], 1, PosixPath('/some/path.txt'), 3.14]
|
|
45
|
+
|
|
46
|
+
# EZList save to zarr store:
|
|
47
|
+
>>> ez_list.save("path/to/store", name="saved_list")
|
|
48
|
+
|
|
49
|
+
# EZList load from zarr store:
|
|
50
|
+
>>> EZList.open("path/to/store", name="saved_list")
|
|
51
|
+
EZList[[1, 2, 3], 1, PosixPath('/some/path.txt'), 3.14]
|
|
52
|
+
|
|
53
|
+
# EZList conversion to a regular list:
|
|
54
|
+
>>> ez_list.copy()
|
|
55
|
+
[[1, 2, 3], 1, PosixPath('some/path.txt'), 3.14]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## EZDict
|
|
59
|
+
An EZDict is a proxy for a Python dict built on top of a zarr.Group.
|
|
60
|
+
It expands zarr.Groups to store and retrieve arbitrary objects.
|
|
61
|
+
|
|
62
|
+
### Examples
|
|
63
|
+
```python
|
|
64
|
+
>>> from ezarr import EZDict
|
|
65
|
+
|
|
66
|
+
# EZdDct creation for a zarr.Group:
|
|
67
|
+
>>> EZDict(zarr.open_group({}))
|
|
68
|
+
EZDict{}
|
|
69
|
+
|
|
70
|
+
# EZDict creation for a dict:
|
|
71
|
+
>>> data = {"a": 1, "b": [1, 2, 3], "c": {"d": "some text"}}
|
|
72
|
+
>>> ez_dict = EZDict.from_dict(data, store={})
|
|
73
|
+
# ~~ or any zarr StoreLike object to store the dict into # |
|
|
74
|
+
|
|
75
|
+
# EZDict lazy representation:
|
|
76
|
+
>>> ez_dict
|
|
77
|
+
EZDict{
|
|
78
|
+
a: 1,
|
|
79
|
+
b: [1 2 3],
|
|
80
|
+
c: {...} # <-- note the nested dict was not loaded for the representation
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# EZDict save to zarr store:
|
|
84
|
+
>>> ez_dict.save("path/to/store", name="saved_dict")
|
|
85
|
+
|
|
86
|
+
# EZDict load from zarr store:
|
|
87
|
+
>>> EZDict.open("path/to/store", name="saved_dict")
|
|
88
|
+
EZDict{
|
|
89
|
+
a: 1,
|
|
90
|
+
b: [1 2 3],
|
|
91
|
+
c: {...}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# EZDict conversion to a regular dict:
|
|
95
|
+
>>> ez_dict.copy()
|
|
96
|
+
{'a': np.int64(1), 'b': array([1, 2, 3]), 'c': {'d': np.str_('some text')}}
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## SupportsEZReadWrite
|
|
101
|
+
The `SupportsEZReadWrite` protocol, once implemented on an object, allows it to be store in and retrieved from a zarr hierarchy.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
@runtime_checkable
|
|
105
|
+
class SupportsEZRead(Protocol):
|
|
106
|
+
@classmethod
|
|
107
|
+
def __ez_read__(cls, grp: zarr.Group) -> Self: ...
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@runtime_checkable
|
|
111
|
+
class SupportsEZWrite(Protocol):
|
|
112
|
+
def __ez_write__(self, grp: zarr.Group) -> None: ...
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@runtime_checkable
|
|
116
|
+
class SupportsEZReadWrite(SupportsEZRead, SupportsEZWrite, Protocol): ...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This protocol has 2 parts:
|
|
120
|
+
- `SupportsEZRead` defines the classmethod `__ez_read__(cls, grp: zarr.Group) -> Self` for reconstructing an object from a zarr.Group.
|
|
121
|
+
- `SupportsEZWrite` defines the method `__ez_write__(self, grp: zarr.Group) -> None` for storing an object in a zarr.Group.
|
|
122
|
+
|
|
123
|
+
Once implemented on an object, this protocol allows that object to be automatically saved and loaded in EZLists and EZDicts.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import itertools as it
|
|
2
|
+
import math
|
|
3
|
+
from typing import Any, cast
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import numpy.typing as npt
|
|
7
|
+
import zarr # pyright: ignore[reportMissingTypeStubs]
|
|
8
|
+
|
|
9
|
+
import ezarr
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def repr_element(elem: Any, prefix: str = "") -> str:
|
|
13
|
+
if isinstance(elem, np.str_):
|
|
14
|
+
return f"'{elem}'"
|
|
15
|
+
|
|
16
|
+
if isinstance(elem, np.generic):
|
|
17
|
+
return str(elem)
|
|
18
|
+
|
|
19
|
+
if isinstance(elem, zarr.Group | ezarr.EZDict):
|
|
20
|
+
return "{...}" if len(elem) else "{}" # pyright: ignore[reportUnknownArgumentType]
|
|
21
|
+
|
|
22
|
+
if isinstance(elem, zarr.Array):
|
|
23
|
+
return repr_array(elem, prefix=prefix)
|
|
24
|
+
|
|
25
|
+
return repr(elem)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def repr_array(arr: zarr.Array, prefix: str = "") -> str:
|
|
29
|
+
if arr.size == 0:
|
|
30
|
+
return "[]"
|
|
31
|
+
|
|
32
|
+
if arr.ndim == 0:
|
|
33
|
+
return repr_element(arr[()])
|
|
34
|
+
|
|
35
|
+
data = cast(npt.NDArray[Any], arr.get_orthogonal_selection(tuple(_get_index(shape) for shape in arr.shape)))
|
|
36
|
+
|
|
37
|
+
if np.issubdtype(data.dtype, np.integer):
|
|
38
|
+
align = math.ceil(math.log10(data.max()))
|
|
39
|
+
|
|
40
|
+
elif np.issubdtype(data.dtype, np.floating):
|
|
41
|
+
data = np.vectorize(np.format_float_positional)(data)
|
|
42
|
+
align = np.vectorize(len)(data).max()
|
|
43
|
+
|
|
44
|
+
else:
|
|
45
|
+
data = data.astype(str)
|
|
46
|
+
align = np.vectorize(len)(data)
|
|
47
|
+
|
|
48
|
+
return _print3(data, list(arr.shape), align=align, prefix=prefix, ignore_prefix=True)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_index(axis_shape: int) -> list[int]:
|
|
52
|
+
if axis_shape <= 6:
|
|
53
|
+
return list(range(axis_shape))
|
|
54
|
+
|
|
55
|
+
return [0, 1, 2, -3, -2, -1]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _print3(data: np.ndarray, shape: list[int], align: int, *, prefix: str = "", ignore_prefix: bool = False) -> str:
|
|
59
|
+
if data.ndim == 1:
|
|
60
|
+
if shape[0] <= 6:
|
|
61
|
+
values = data
|
|
62
|
+
else:
|
|
63
|
+
values = it.chain(data[:3], ["..."], data[3:])
|
|
64
|
+
|
|
65
|
+
return f"{'' if ignore_prefix else prefix}[{' '.join(map(lambda v: f'{v:>{align}}', values))}]"
|
|
66
|
+
|
|
67
|
+
if shape[0] <= 6:
|
|
68
|
+
rows = [
|
|
69
|
+
_print3(data[idx], shape[1:], align, prefix=prefix + " ", ignore_prefix=idx == 0) for idx in range(shape[0])
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
else:
|
|
73
|
+
rows = [
|
|
74
|
+
_print3(data[0], shape[1:], align, prefix=prefix + " ", ignore_prefix=True),
|
|
75
|
+
_print3(data[1], shape[1:], align, prefix=prefix + " "),
|
|
76
|
+
_print3(data[2], shape[1:], align, prefix=prefix + " "),
|
|
77
|
+
f"{prefix} ...",
|
|
78
|
+
_print3(data[3], shape[1:], align, prefix=prefix + " "),
|
|
79
|
+
_print3(data[4], shape[1:], align, prefix=prefix + " "),
|
|
80
|
+
_print3(data[5], shape[1:], align, prefix=prefix + " "),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
sep = "," + "\n" * max(1, len(shape) - 1)
|
|
84
|
+
return f"{'' if ignore_prefix else prefix}[{sep.join(rows)}]"
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools as it
|
|
4
|
+
from collections.abc import ItemsView, Iterator, Mapping, MutableMapping
|
|
5
|
+
from typing import Any, Self, cast, override
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import numpy.typing as npt
|
|
10
|
+
import zarr
|
|
11
|
+
from numpy._typing import _SupportsArray as SupportsArray # pyright: ignore[reportPrivateUsage]
|
|
12
|
+
from zarr.errors import UnstableSpecificationWarning
|
|
13
|
+
from zarr.storage import StoreLike # pyright: ignore[reportUnknownVariableType]
|
|
14
|
+
|
|
15
|
+
import ezarr
|
|
16
|
+
from ezarr import io
|
|
17
|
+
from ezarr._repr import repr_element
|
|
18
|
+
from ezarr.names import AccessModeLiteral
|
|
19
|
+
from ezarr.object import EZObject
|
|
20
|
+
|
|
21
|
+
type DictData[T] = Mapping[str, int | float | np.integer | np.floating | list[Any] | npt.ArrayLike | DictData[T]]
|
|
22
|
+
type GroupItems = ItemsView[str, zarr.Group | zarr.Array]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EZDict[T](EZObject[T, dict[str, T]], MutableMapping[str, T]):
|
|
26
|
+
"""
|
|
27
|
+
Dict-like object wrapping a zarr.Group for storing arbitrary Python objects
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
group: a zarr.Group
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> EZDict(zarr.open_group({}))
|
|
34
|
+
EZDict{}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, group: zarr.Group) -> None:
|
|
38
|
+
self._group: zarr.Group = group
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_dict(
|
|
42
|
+
cls,
|
|
43
|
+
dct: Mapping[str, Any],
|
|
44
|
+
*,
|
|
45
|
+
name: str = "",
|
|
46
|
+
store: StoreLike | None = None,
|
|
47
|
+
mode: AccessModeLiteral = "a",
|
|
48
|
+
path: str | None = None,
|
|
49
|
+
overwrite: bool = False,
|
|
50
|
+
) -> Self:
|
|
51
|
+
r"""
|
|
52
|
+
Create an EZDict from a regular in-memory Python dictionary.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
dct: dictionary with arbitrary data to store.
|
|
56
|
+
name: name for the EZDict, to use inside the store.
|
|
57
|
+
store: Store or path to directory in file system or nam of zip file.
|
|
58
|
+
mode: Persistence mode, in ['r', 'r+', 'a', 'w', 'w-'].
|
|
59
|
+
path: Group path within store.
|
|
60
|
+
overwrite: overwrite object if a group with name `name` already exists ? (default: False)
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
>>> data = {"a": 1, "b": [1, 2, 3], "c": {"d": "some text"}}
|
|
64
|
+
>>> ez_dict = EZDict.from_dict(data)
|
|
65
|
+
>>> repr(ez_dict)
|
|
66
|
+
'EZDict{\n\ta: 1,\n\tb: [1 2 3],\n\tc: {...}\n}'
|
|
67
|
+
"""
|
|
68
|
+
if store is None:
|
|
69
|
+
store = {}
|
|
70
|
+
|
|
71
|
+
grp = zarr.open_group(store, mode=mode, path=path)
|
|
72
|
+
|
|
73
|
+
if name:
|
|
74
|
+
grp = grp.create_group(name, overwrite=overwrite)
|
|
75
|
+
|
|
76
|
+
to_visit: list[tuple[Any, str, str]] = [(dct[k], k, "") for k in dct.keys()]
|
|
77
|
+
|
|
78
|
+
while len(to_visit):
|
|
79
|
+
value, name_, path_ = to_visit.pop()
|
|
80
|
+
|
|
81
|
+
if isinstance(value, Mapping):
|
|
82
|
+
to_visit.extend([(value[k], k, f"{path_}/{name_}" if path_ else name_) for k in value.keys()]) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
|
|
83
|
+
|
|
84
|
+
elif isinstance(value, list | SupportsArray):
|
|
85
|
+
grp.require_group(path_).create_array(name_, data=np.asarray(value), overwrite=overwrite) # pyright: ignore[reportUnknownArgumentType]
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
io.write_object(grp, obj=value, name=name_, path=path_, overwrite=overwrite)
|
|
89
|
+
|
|
90
|
+
return cls(grp)
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _repr(grp: zarr.Group) -> str:
|
|
94
|
+
if not len(grp):
|
|
95
|
+
return "{}"
|
|
96
|
+
|
|
97
|
+
if len(grp) > 100:
|
|
98
|
+
return (
|
|
99
|
+
"{\n\t"
|
|
100
|
+
+ ",\n\t".join(
|
|
101
|
+
[
|
|
102
|
+
f"{name}: {repr_element(io.read_object(grp, name=name), prefix=f'\t{" " * len(name)} ')}"
|
|
103
|
+
for name in it.islice(sorted(grp.keys()), 0, 10)
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
+ ",\n\t...,\n\t"
|
|
107
|
+
+ ",\n\t".join(
|
|
108
|
+
[
|
|
109
|
+
f"{name}: {repr_element(io.read_object(grp, name=name), prefix=f'\t{" " * len(name)} ')}"
|
|
110
|
+
for name in it.islice(sorted(grp.keys()), len(grp) - 10, None)
|
|
111
|
+
]
|
|
112
|
+
)
|
|
113
|
+
+ "}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
"{\n\t"
|
|
118
|
+
+ ",\n\t".join(
|
|
119
|
+
[
|
|
120
|
+
f"{name}: {repr_element(io.read_object(grp, name=name), prefix=f'\t{" " * len(name)} ')}"
|
|
121
|
+
for name in sorted(grp.keys())
|
|
122
|
+
]
|
|
123
|
+
)
|
|
124
|
+
+ "\n}"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
@override
|
|
128
|
+
def __repr__(self) -> str:
|
|
129
|
+
return f"{type(self).__name__}{self._repr(self._group)}"
|
|
130
|
+
|
|
131
|
+
@override
|
|
132
|
+
def __len__(self) -> int:
|
|
133
|
+
return len(self._group)
|
|
134
|
+
|
|
135
|
+
@override
|
|
136
|
+
def __getitem__(self, key: str, /) -> T:
|
|
137
|
+
return io.read_object(self._group, name=key)
|
|
138
|
+
|
|
139
|
+
@override
|
|
140
|
+
def __setitem__(self, key: str, value: T, /) -> None:
|
|
141
|
+
if key in self:
|
|
142
|
+
try:
|
|
143
|
+
# try to compare values, might fail when comparing arrays with different shapes
|
|
144
|
+
if self[key] == value:
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
except ValueError:
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
with warnings.catch_warnings(action="ignore", category=UnstableSpecificationWarning):
|
|
151
|
+
io.write_object(self._group, obj=value, name=key, overwrite=True)
|
|
152
|
+
|
|
153
|
+
@override
|
|
154
|
+
def __delitem__(self, key: str, /) -> None:
|
|
155
|
+
del self._group[key]
|
|
156
|
+
|
|
157
|
+
@override
|
|
158
|
+
def __iter__(self) -> Iterator[str]:
|
|
159
|
+
yield from sorted(self._group.keys())
|
|
160
|
+
|
|
161
|
+
def __deepcopy__(self, _memo: dict[Any, Any]) -> dict[str, Any]:
|
|
162
|
+
return self.copy()
|
|
163
|
+
|
|
164
|
+
def __ior__(self, other: object) -> EZDict[T]:
|
|
165
|
+
if not isinstance(other, Mapping):
|
|
166
|
+
raise NotImplementedError
|
|
167
|
+
|
|
168
|
+
other = cast(Mapping[str, T], other)
|
|
169
|
+
for name, value in other.items():
|
|
170
|
+
if isinstance(value, Mapping):
|
|
171
|
+
grp = self._group.require_group(name)
|
|
172
|
+
EZDict(grp).__ior__(value) # pyright: ignore[reportUnknownArgumentType]
|
|
173
|
+
|
|
174
|
+
else:
|
|
175
|
+
with warnings.catch_warnings(action="ignore", category=UnstableSpecificationWarning):
|
|
176
|
+
self[name] = value
|
|
177
|
+
|
|
178
|
+
return self
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
@override
|
|
182
|
+
def open(
|
|
183
|
+
cls, store: StoreLike | None = None, *, name: str = "", mode: AccessModeLiteral = "a", path: str | None = None
|
|
184
|
+
) -> Self:
|
|
185
|
+
r"""
|
|
186
|
+
Open this EZDict from a store.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
store: Store, path to a directory or name of a zip file.
|
|
190
|
+
name: name for the EZDict, to use inside the store.
|
|
191
|
+
mode: Persistence mode.
|
|
192
|
+
path: path within the store to open.
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
>>> ez_dict = EZDict.from_dict({"a": 1, "b": [1, 2, 3], "c": {"d": "some text"}})
|
|
196
|
+
>>> ez_dict.save("/tmp/dict", overwrite=True)
|
|
197
|
+
>>> repr(ez_dict.open("/tmp/dict"))
|
|
198
|
+
'EZDict{\n\ta: 1,\n\tb: [1 2 3]\n}'
|
|
199
|
+
"""
|
|
200
|
+
path = f"{path.rstrip('/')}/{name}" if path else name
|
|
201
|
+
return cls(zarr.open_group(store, mode=mode, path=path))
|
|
202
|
+
|
|
203
|
+
@override
|
|
204
|
+
def save(
|
|
205
|
+
self,
|
|
206
|
+
store: StoreLike,
|
|
207
|
+
*,
|
|
208
|
+
name: str = "",
|
|
209
|
+
mode: AccessModeLiteral = "a",
|
|
210
|
+
path: str | None = None,
|
|
211
|
+
overwrite: bool = False,
|
|
212
|
+
) -> None:
|
|
213
|
+
"""
|
|
214
|
+
Save this EZDict to a local file system.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
store: Store, path to a directory or name of a zip file.
|
|
218
|
+
name: name for the EZDict, to use inside the store.
|
|
219
|
+
mode: Persistence mode.
|
|
220
|
+
path: path within the store where the EZDict will be saved.
|
|
221
|
+
overwrite: overwrite EZDict if a group with name `name` already exists ? (default: False)
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
>>> from pathlib import Path
|
|
225
|
+
>>> ez_dict = EZDict.from_dict({"a": 1, "b": [1, 2, 3], "c": {"d": "some text"}})
|
|
226
|
+
>>> ez_dict.save("/tmp/dict", overwrite=True)
|
|
227
|
+
>>> Path("/tmp/dict").exists()
|
|
228
|
+
True
|
|
229
|
+
"""
|
|
230
|
+
path = f"{path.rstrip('/')}/{name}/" if path else f"{name}/"
|
|
231
|
+
|
|
232
|
+
for _, array in self._group.arrays():
|
|
233
|
+
assert isinstance(array, zarr.Array)
|
|
234
|
+
zarr.create_array(store, name=path + array.path, data=array, overwrite=overwrite) # pyright: ignore[reportArgumentType]
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def _copy_nested(dct: dict[str, Any], value: EZDict[Any]) -> dict[str, Any]:
|
|
238
|
+
for k, v in value.items():
|
|
239
|
+
if isinstance(v, EZDict):
|
|
240
|
+
sub = dct.setdefault(k, {})
|
|
241
|
+
EZDict._copy_nested(sub, v) # pyright: ignore[reportUnknownArgumentType]
|
|
242
|
+
|
|
243
|
+
elif isinstance(v, ezarr.EZList):
|
|
244
|
+
dct[k] = v.copy()
|
|
245
|
+
|
|
246
|
+
elif isinstance(v, zarr.Array):
|
|
247
|
+
dct[k] = np.array(v)
|
|
248
|
+
|
|
249
|
+
else:
|
|
250
|
+
dct[k] = v
|
|
251
|
+
|
|
252
|
+
return dct
|
|
253
|
+
|
|
254
|
+
@override
|
|
255
|
+
def copy(self) -> dict[str, T]:
|
|
256
|
+
"""
|
|
257
|
+
Convert this EZDict into a Python dict, loading all the data into memory.
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
>>> ez_dict = EZDict.from_dict({"a": 1, "b": [1, 2, 3], "c": {"d": "some text"}})
|
|
261
|
+
>>> ez_dict.copy()
|
|
262
|
+
{'a': np.int64(1), 'b': array([1, 2, 3]), 'c': {'d': np.str_('some text')}}
|
|
263
|
+
"""
|
|
264
|
+
return EZDict._copy_nested({}, self)
|