Skip to content

Commit bdd433a

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 and 0f89a96
1 parent 33ec64a commit bdd433a

14 files changed

+680
-447
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: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,21 @@
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

18+
from typing import Union
19+
20+
StoreLike = Union[Store, str, None]
21+
1822

1923
# noinspection PyShadowingBuiltins
20-
def open(store=None, mode='a', **kwargs):
24+
def open(store: StoreLike = None, mode: str = "a", **kwargs):
2125
"""Convenience function to open a group or array using file-mode-like semantics.
2226
2327
Parameters
2428
----------
25-
store : MutableMapping or string, optional
29+
store : Store or string, optional
2630
Store or path to directory in file system or name of zip file.
2731
mode : {'r', 'r+', 'a', 'w', 'w-'}, optional
2832
Persistence mode: 'r' means read only (must exist); 'r+' means
@@ -75,32 +79,33 @@ def open(store=None, mode='a', **kwargs):
7579
clobber = mode == 'w'
7680
# we pass storage options explicitly, since normalize_store_arg might construct
7781
# a store if the input is a fsspec-compatible URL
78-
store = normalize_store_arg(store, clobber=clobber,
79-
storage_options=kwargs.pop("storage_options", {}))
82+
_store: Store = normalize_store_arg(
83+
store, clobber=clobber, storage_options=kwargs.pop("storage_options", {})
84+
)
8085
path = normalize_storage_path(path)
8186

8287
if mode in {'w', 'w-', 'x'}:
8388
if 'shape' in kwargs:
84-
return open_array(store, mode=mode, **kwargs)
89+
return open_array(_store, mode=mode, **kwargs)
8590
else:
86-
return open_group(store, mode=mode, **kwargs)
91+
return open_group(_store, mode=mode, **kwargs)
8792

8893
elif mode == "a":
89-
if "shape" in kwargs or contains_array(store, path):
90-
return open_array(store, mode=mode, **kwargs)
94+
if "shape" in kwargs or contains_array(_store, path):
95+
return open_array(_store, mode=mode, **kwargs)
9196
else:
92-
return open_group(store, mode=mode, **kwargs)
97+
return open_group(_store, mode=mode, **kwargs)
9398

9499
else:
95-
if contains_array(store, path):
96-
return open_array(store, mode=mode, **kwargs)
97-
elif contains_group(store, path):
98-
return open_group(store, mode=mode, **kwargs)
100+
if contains_array(_store, path):
101+
return open_array(_store, mode=mode, **kwargs)
102+
elif contains_group(_store, path):
103+
return open_group(_store, mode=mode, **kwargs)
99104
else:
100105
raise PathNotFoundError(path)
101106

102107

103-
def save_array(store, arr, **kwargs):
108+
def save_array(store: StoreLike, arr, **kwargs):
104109
"""Convenience function to save a NumPy array to the local file system, following a
105110
similar API to the NumPy save() function.
106111
@@ -132,16 +137,16 @@ def save_array(store, arr, **kwargs):
132137
133138
"""
134139
may_need_closing = isinstance(store, str)
135-
store = normalize_store_arg(store, clobber=True)
140+
_store: Store = normalize_store_arg(store, clobber=True)
136141
try:
137-
_create_array(arr, store=store, overwrite=True, **kwargs)
142+
_create_array(arr, store=_store, overwrite=True, **kwargs)
138143
finally:
139-
if may_need_closing and hasattr(store, 'close'):
144+
if may_need_closing:
140145
# needed to ensure zip file records are written
141-
store.close()
146+
_store.close()
142147

143148

144-
def save_group(store, *args, **kwargs):
149+
def save_group(store: StoreLike, *args, **kwargs):
145150
"""Convenience function to save several NumPy arrays to the local file system, following a
146151
similar API to the NumPy savez()/savez_compressed() functions.
147152
@@ -203,21 +208,21 @@ def save_group(store, *args, **kwargs):
203208
raise ValueError('at least one array must be provided')
204209
# handle polymorphic store arg
205210
may_need_closing = isinstance(store, str)
206-
store = normalize_store_arg(store, clobber=True)
211+
_store: Store = normalize_store_arg(store, clobber=True)
207212
try:
208-
grp = _create_group(store, overwrite=True)
213+
grp = _create_group(_store, overwrite=True)
209214
for i, arr in enumerate(args):
210215
k = 'arr_{}'.format(i)
211216
grp.create_dataset(k, data=arr, overwrite=True)
212217
for k, arr in kwargs.items():
213218
grp.create_dataset(k, data=arr, overwrite=True)
214219
finally:
215-
if may_need_closing and hasattr(store, 'close'):
220+
if may_need_closing:
216221
# needed to ensure zip file records are written
217-
store.close()
222+
_store.close()
218223

219224

220-
def save(store, *args, **kwargs):
225+
def save(store: StoreLike, *args, **kwargs):
221226
"""Convenience function to save an array or group of arrays to the local file system.
222227
223228
Parameters
@@ -327,7 +332,7 @@ def __repr__(self):
327332
return r
328333

329334

330-
def load(store):
335+
def load(store: StoreLike):
331336
"""Load data from an array or group into memory.
332337
333338
Parameters
@@ -353,11 +358,11 @@ def load(store):
353358
354359
"""
355360
# handle polymorphic store arg
356-
store = normalize_store_arg(store)
357-
if contains_array(store, path=None):
358-
return Array(store=store, path=None)[...]
359-
elif contains_group(store, path=None):
360-
grp = Group(store=store, path=None)
361+
_store = normalize_store_arg(store)
362+
if contains_array(_store, path=None):
363+
return Array(store=_store, path=None)[...]
364+
elif contains_group(_store, path=None):
365+
grp = Group(store=_store, path=None)
361366
return LazyLoader(grp)
362367

363368

@@ -1073,7 +1078,7 @@ def copy_all(source, dest, shallow=False, without_attrs=False, log=None,
10731078
return n_copied, n_skipped, n_bytes_copied
10741079

10751080

1076-
def consolidate_metadata(store, metadata_key='.zmetadata'):
1081+
def consolidate_metadata(store: Store, metadata_key=".zmetadata"):
10771082
"""
10781083
Consolidate all metadata for groups and arrays within the given store
10791084
into a single resource and put it under the given key.
@@ -1124,7 +1129,7 @@ def is_zarr_key(key):
11241129
return open_consolidated(store, metadata_key=metadata_key)
11251130

11261131

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

zarr/core.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
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, ArrayIndexError
@@ -29,7 +31,7 @@
2931
pop_fields,
3032
)
3133
from zarr.meta import decode_array_metadata, encode_array_metadata
32-
from zarr.storage import array_meta_key, attrs_key, getsize, listdir
34+
from zarr.storage import array_meta_key, attrs_key, getsize, listdir, Store
3335
from zarr.util import (
3436
InfoReporter,
3537
check_array_shape,
@@ -129,7 +131,7 @@ class Array:
129131

130132
def __init__(
131133
self,
132-
store,
134+
store: Store,
133135
path=None,
134136
read_only=False,
135137
chunk_store=None,
@@ -141,6 +143,9 @@ def __init__(
141143
# N.B., expect at this point store is fully initialized with all
142144
# configuration metadata fully specified and normalized
143145

146+
store = Store._ensure_store(store)
147+
chunk_store = Store._ensure_store(chunk_store)
148+
144149
self._store = store
145150
self._chunk_store = chunk_store
146151
self._path = normalize_storage_path(path)
@@ -1955,7 +1960,7 @@ def _encode_chunk(self, chunk):
19551960
cdata = chunk
19561961

19571962
# ensure in-memory data is immutable and easy to compare
1958-
if isinstance(self.chunk_store, dict):
1963+
if isinstance(self.chunk_store, MutableMapping):
19591964
cdata = ensure_bytes(cdata)
19601965

19611966
return cdata
@@ -1988,10 +1993,10 @@ def info(self):
19881993
Order : C
19891994
Read-only : False
19901995
Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
1991-
Store type : builtins.dict
1996+
Store type : zarr.storage.KVStore
19921997
No. bytes : 4000000 (3.8M)
1993-
No. bytes stored : ...
1994-
Storage ratio : ...
1998+
No. bytes stored : 320
1999+
Storage ratio : 12500.0
19952000
Chunks initialized : 0/10
19962001
19972002
"""

zarr/creation.py

Lines changed: 17 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,9 @@ def create(shape, chunks=True, dtype=None, compressor='default',
129139
return z
130140

131141

132-
def normalize_store_arg(store, clobber=False, storage_options=None, mode='w'):
142+
def normalize_store_arg(store, clobber=False, storage_options=None, mode="w") -> Store:
133143
if store is None:
134-
return dict()
144+
return Store._ensure_store(dict())
135145
elif isinstance(store, str):
136146
mode = mode if clobber else "r"
137147
if "://" in store or "::" in store:
@@ -145,6 +155,8 @@ def normalize_store_arg(store, clobber=False, storage_options=None, mode='w'):
145155
else:
146156
return DirectoryStore(store)
147157
else:
158+
if not isinstance(store, Store) and isinstance(store, MutableMapping):
159+
store = KVStore(store)
148160
return store
149161

150162

0 commit comments

Comments
 (0)