Skip to content

Commit 0437842

Browse files
committed
Introduce a base store Class
Unconditionally close store in tests. All the tested stores should now have a `close()` method we can call and will be no-op if the stores do not need closing. Turn UserWarnings into errors And turn then back into only warnings into relevant tests. This ensure that we are not using deprecated functionalities, except when testing for it. initially based on 318eddcd, and later 1249f35
1 parent 06450d0 commit 0437842

14 files changed

+640
-425
lines changed

docs/tutorial.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ print some diagnostics, e.g.::
176176
Read-only : False
177177
Compressor : Blosc(cname='zstd', clevel=3, shuffle=BITSHUFFLE,
178178
: blocksize=0)
179-
Store type : builtins.dict
179+
Store type : zarr.storage.KVStore
180180
No. bytes : 400000000 (381.5M)
181181
No. bytes stored : 3379344 (3.2M)
182182
Storage ratio : 118.4
@@ -268,7 +268,7 @@ Here is an example using a delta filter with the Blosc compressor::
268268
Read-only : False
269269
Filter [0] : Delta(dtype='<i4')
270270
Compressor : Blosc(cname='zstd', clevel=1, shuffle=SHUFFLE, blocksize=0)
271-
Store type : builtins.dict
271+
Store type : zarr.storage.KVStore
272272
No. bytes : 400000000 (381.5M)
273273
No. bytes stored : 1290562 (1.2M)
274274
Storage ratio : 309.9
@@ -795,8 +795,10 @@ Here is an example using S3Map to read an array created previously::
795795
Order : C
796796
Read-only : False
797797
Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
798-
Store type : fsspec.mapping.FSMap
798+
Store type : zarr.storage.KVStore
799799
No. bytes : 21
800+
No. bytes stored : 382
801+
Storage ratio : 0.1
800802
Chunks initialized : 3/3
801803
>>> z[:]
802804
array([b'H', b'e', b'l', b'l', b'o', b' ', b'f', b'r', b'o', b'm', b' ',
@@ -1262,7 +1264,7 @@ ratios, depending on the correlation structure within the data. E.g.::
12621264
Order : C
12631265
Read-only : False
12641266
Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
1265-
Store type : builtins.dict
1267+
Store type : zarr.storage.KVStore
12661268
No. bytes : 400000000 (381.5M)
12671269
No. bytes stored : 6696010 (6.4M)
12681270
Storage ratio : 59.7
@@ -1276,7 +1278,7 @@ ratios, depending on the correlation structure within the data. E.g.::
12761278
Order : F
12771279
Read-only : False
12781280
Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
1279-
Store type : builtins.dict
1281+
Store type : zarr.storage.KVStore
12801282
No. bytes : 400000000 (381.5M)
12811283
No. bytes stored : 4684636 (4.5M)
12821284
Storage ratio : 85.4

mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[mypy]
2-
python_version = 3.6
2+
python_version = 3.8
33
ignore_missing_imports = True
44
follow_imports = silent

pytest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS IGNORE_EXCEPTION_DETAIL
33
addopts = --durations=10
44
filterwarnings =
55
error::DeprecationWarning:zarr.*
6+
error::UserWarning:zarr.*
67
ignore:PY_SSIZE_T_CLEAN will be required.*:DeprecationWarning
8+
ignore:The loop argument is deprecated since Python 3.8.*:DeprecationWarning

zarr/convenience.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from zarr.hierarchy import group as _create_group
1313
from zarr.hierarchy import open_group
1414
from zarr.meta import json_dumps, json_loads
15-
from zarr.storage import contains_array, contains_group
15+
from zarr.storage import contains_array, contains_group, Store
1616
from zarr.util import TreeViewer, buffer_size, normalize_storage_path
1717

1818

@@ -73,7 +73,7 @@ def open(store=None, mode='a', **kwargs):
7373
path = kwargs.get('path', None)
7474
# handle polymorphic store arg
7575
clobber = mode == 'w'
76-
store = normalize_store_arg(store, clobber=clobber)
76+
store: Store = normalize_store_arg(store, clobber=clobber)
7777
path = normalize_storage_path(path)
7878

7979
if mode in {'w', 'w-', 'x'}:
@@ -97,7 +97,7 @@ def open(store=None, mode='a', **kwargs):
9797
raise PathNotFoundError(path)
9898

9999

100-
def save_array(store, arr, **kwargs):
100+
def save_array(store: Store, arr, **kwargs):
101101
"""Convenience function to save a NumPy array to the local file system, following a
102102
similar API to the NumPy save() function.
103103
@@ -138,7 +138,7 @@ def save_array(store, arr, **kwargs):
138138
store.close()
139139

140140

141-
def save_group(store, *args, **kwargs):
141+
def save_group(store: Store, *args, **kwargs):
142142
"""Convenience function to save several NumPy arrays to the local file system, following a
143143
similar API to the NumPy savez()/savez_compressed() functions.
144144
@@ -214,7 +214,7 @@ def save_group(store, *args, **kwargs):
214214
store.close()
215215

216216

217-
def save(store, *args, **kwargs):
217+
def save(store: Store, *args, **kwargs):
218218
"""Convenience function to save an array or group of arrays to the local file system.
219219
220220
Parameters
@@ -324,7 +324,7 @@ def __repr__(self):
324324
return r
325325

326326

327-
def load(store):
327+
def load(store: Store):
328328
"""Load data from an array or group into memory.
329329
330330
Parameters
@@ -1069,7 +1069,7 @@ def copy_all(source, dest, shallow=False, without_attrs=False, log=None,
10691069
return n_copied, n_skipped, n_bytes_copied
10701070

10711071

1072-
def consolidate_metadata(store, metadata_key='.zmetadata'):
1072+
def consolidate_metadata(store: Store, metadata_key=".zmetadata"):
10731073
"""
10741074
Consolidate all metadata for groups and arrays within the given store
10751075
into a single resource and put it under the given key.
@@ -1120,7 +1120,7 @@ def is_zarr_key(key):
11201120
return open_consolidated(store, metadata_key=metadata_key)
11211121

11221122

1123-
def open_consolidated(store, metadata_key='.zmetadata', mode='r+', **kwargs):
1123+
def open_consolidated(store: Store, metadata_key=".zmetadata", mode="r+", **kwargs):
11241124
"""Open group using metadata previously consolidated into a single key.
11251125
11261126
This is an optimised method for opening a Zarr group, where instead of

zarr/core.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,39 @@
99
import numpy as np
1010
from numcodecs.compat import ensure_bytes, ensure_ndarray
1111

12+
from collections.abc import MutableMapping
13+
1214
from zarr.attrs import Attributes
1315
from zarr.codecs import AsType, get_codec
1416
from zarr.errors import ArrayNotFoundError, ReadOnlyError
15-
from zarr.indexing import (BasicIndexer, CoordinateIndexer, MaskIndexer,
16-
OIndex, OrthogonalIndexer, VIndex, check_fields,
17-
check_no_multi_fields, ensure_tuple,
18-
err_too_many_indices, is_contiguous_selection,
19-
is_scalar, pop_fields)
17+
from zarr.indexing import (
18+
BasicIndexer,
19+
CoordinateIndexer,
20+
MaskIndexer,
21+
OIndex,
22+
OrthogonalIndexer,
23+
VIndex,
24+
check_fields,
25+
check_no_multi_fields,
26+
ensure_tuple,
27+
err_too_many_indices,
28+
is_contiguous_selection,
29+
is_scalar,
30+
pop_fields,
31+
)
2032
from zarr.meta import decode_array_metadata, encode_array_metadata
21-
from zarr.storage import array_meta_key, attrs_key, getsize, listdir
22-
from zarr.util import (InfoReporter, check_array_shape, human_readable_size,
23-
is_total_slice, nolock, normalize_chunks,
24-
normalize_resize_args, normalize_shape,
25-
normalize_storage_path)
33+
from zarr.storage import array_meta_key, attrs_key, getsize, listdir, Store
34+
from zarr.util import (
35+
InfoReporter,
36+
check_array_shape,
37+
human_readable_size,
38+
is_total_slice,
39+
nolock,
40+
normalize_chunks,
41+
normalize_resize_args,
42+
normalize_shape,
43+
normalize_storage_path,
44+
)
2645

2746

2847
# noinspection PyUnresolvedReferences
@@ -107,6 +126,9 @@ def __init__(self, store, path=None, read_only=False, chunk_store=None,
107126
# N.B., expect at this point store is fully initialized with all
108127
# configuration metadata fully specified and normalized
109128

129+
store = Store._ensure_store(store)
130+
chunk_store = Store._ensure_store(chunk_store)
131+
110132
self._store = store
111133
self._chunk_store = chunk_store
112134
self._path = normalize_storage_path(path)
@@ -1851,7 +1873,7 @@ def _encode_chunk(self, chunk):
18511873
cdata = chunk
18521874

18531875
# ensure in-memory data is immutable and easy to compare
1854-
if isinstance(self.chunk_store, dict):
1876+
if isinstance(self.chunk_store, MutableMapping):
18551877
cdata = ensure_bytes(cdata)
18561878

18571879
return cdata
@@ -1884,10 +1906,10 @@ def info(self):
18841906
Order : C
18851907
Read-only : False
18861908
Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
1887-
Store type : builtins.dict
1909+
Store type : zarr.storage.KVStore
18881910
No. bytes : 4000000 (3.8M)
1889-
No. bytes stored : ...
1890-
Storage ratio : ...
1911+
No. bytes stored : 320
1912+
Storage ratio : 12500.0
18911913
Chunks initialized : 0/10
18921914
18931915
"""

zarr/creation.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import numpy as np
44
from numcodecs.registry import codec_registry
5+
from collections.abc import MutableMapping
56

67
from zarr.core import Array
78
from zarr.errors import (
@@ -10,9 +11,18 @@
1011
ContainsGroupError,
1112
)
1213
from zarr.n5 import N5Store
13-
from zarr.storage import (DirectoryStore, ZipStore, contains_array,
14-
contains_group, default_compressor, init_array,
15-
normalize_storage_path, FSStore)
14+
from zarr.storage import (
15+
DirectoryStore,
16+
ZipStore,
17+
KVStore,
18+
contains_array,
19+
contains_group,
20+
default_compressor,
21+
init_array,
22+
normalize_storage_path,
23+
FSStore,
24+
Store,
25+
)
1626

1727

1828
def create(shape, chunks=True, dtype=None, compressor='default',
@@ -129,9 +139,11 @@ def create(shape, chunks=True, dtype=None, compressor='default',
129139
return z
130140

131141

132-
def normalize_store_arg(store, clobber=False, default=dict, storage_options=None):
142+
def normalize_store_arg(
143+
store, clobber=False, default=dict, storage_options=None
144+
) -> Store:
133145
if store is None:
134-
return default()
146+
return Store._ensure_store(default())
135147
elif isinstance(store, str):
136148
mode = 'w' if clobber else 'r'
137149
if "://" in store or "::" in store:
@@ -145,6 +157,8 @@ def normalize_store_arg(store, clobber=False, default=dict, storage_options=None
145157
else:
146158
return DirectoryStore(store)
147159
else:
160+
if not isinstance(store, Store) and isinstance(store, MutableMapping):
161+
store = KVStore(store)
148162
return store
149163

150164

zarr/hierarchy.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,26 @@
1515
ReadOnlyError,
1616
)
1717
from zarr.meta import decode_group_metadata
18-
from zarr.storage import (MemoryStore, attrs_key, contains_array,
19-
contains_group, group_meta_key, init_group, listdir,
20-
rename, rmdir)
21-
from zarr.util import (InfoReporter, TreeViewer, is_valid_python_name, nolock,
22-
normalize_shape, normalize_storage_path)
18+
from zarr.storage import (
19+
MemoryStore,
20+
attrs_key,
21+
contains_array,
22+
contains_group,
23+
group_meta_key,
24+
init_group,
25+
listdir,
26+
rename,
27+
rmdir,
28+
Store,
29+
)
30+
from zarr.util import (
31+
InfoReporter,
32+
TreeViewer,
33+
is_valid_python_name,
34+
nolock,
35+
normalize_shape,
36+
normalize_storage_path,
37+
)
2338

2439

2540
class Group(MutableMapping):
@@ -96,6 +111,8 @@ class Group(MutableMapping):
96111

97112
def __init__(self, store, path=None, read_only=False, chunk_store=None,
98113
cache_attrs=True, synchronizer=None):
114+
store = Store._ensure_store(store)
115+
chunk_store = Store._ensure_store(chunk_store)
99116
self._store = store
100117
self._chunk_store = chunk_store
101118
self._path = normalize_storage_path(path)
@@ -237,11 +254,8 @@ def __enter__(self):
237254
return self
238255

239256
def __exit__(self, exc_type, exc_val, exc_tb):
240-
"""If the underlying Store has a ``close`` method, call it."""
241-
try:
242-
self.store.close()
243-
except AttributeError:
244-
pass
257+
"""If the underlying Store should always heave a ``close`` method, call it."""
258+
self.store.close()
245259

246260
def info_items(self):
247261

@@ -1036,7 +1050,7 @@ def move(self, source, dest):
10361050

10371051

10381052
def _normalize_store_arg(store, clobber=False, storage_options=None):
1039-
return normalize_store_arg(store, clobber=clobber, default=MemoryStore,
1053+
return normalize_store_arg(store, clobber=clobber, default=MemoryStore, return normalize_store_arg(
10401054
storage_options=storage_options)
10411055

10421056

0 commit comments

Comments
 (0)