Skip to content

Commit 759acab

Browse files
committed
improve hierarchy test coverage
1 parent ac30331 commit 759acab

File tree

3 files changed

+127
-34
lines changed

3 files changed

+127
-34
lines changed

zarr/hierarchy.py

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ def __init__(self, store, path=None, readonly=False):
8787
self._meta = meta
8888

8989
# setup attributes
90-
self._attrs = Attributes(store, key=self.attrs_key, readonly=readonly)
90+
akey = self._key_prefix + attrs_key
91+
self._attrs = Attributes(store, key=akey, readonly=readonly)
9192

9293
@property
9394
def store(self):
@@ -99,22 +100,6 @@ def path(self):
99100
"""TODO doc me"""
100101
return self._path
101102

102-
@property
103-
def meta_key(self):
104-
"""TODO doc me"""
105-
if self.path:
106-
return self.path + '/' + group_meta_key
107-
else:
108-
return group_meta_key
109-
110-
@property
111-
def attrs_key(self):
112-
"""TODO doc me"""
113-
if self.path:
114-
return self.path + '/' + attrs_key
115-
else:
116-
return attrs_key
117-
118103
@property
119104
def readonly(self):
120105
"""TODO"""
@@ -209,7 +194,7 @@ def __setitem__(self, key, value):
209194
raise NotImplementedError()
210195

211196
def keys(self):
212-
for key in listdir(self.store, self.path):
197+
for key in sorted(listdir(self.store, self.path)):
213198
path = self.path + '/' + key
214199
if (contains_array(self.store, path) or
215200
contains_group(self.store, path)):
@@ -219,33 +204,33 @@ def values(self):
219204
return (v for _, v in self.items())
220205

221206
def items(self):
222-
for key in listdir(self.store, self.path):
207+
for key in sorted(listdir(self.store, self.path)):
223208
path = self.path + '/' + key
224209
if contains_array(self.store, path):
225210
yield key, Array(self.store, path=path, readonly=self.readonly)
226211
elif contains_group(self.store, path):
227212
yield key, Group(self.store, path=path, readonly=self.readonly)
228213

229214
def group_keys(self):
230-
for key in listdir(self.store, self.path):
215+
for key in sorted(listdir(self.store, self.path)):
231216
path = self.path + '/' + key
232217
if contains_group(self.store, path):
233218
yield key
234219

235220
def groups(self):
236-
for key in listdir(self.store, self.path):
221+
for key in sorted(listdir(self.store, self.path)):
237222
path = self.path + '/' + key
238223
if contains_group(self.store, path):
239224
yield key, Group(self.store, path=path, readonly=self.readonly)
240225

241226
def array_keys(self):
242-
for key in listdir(self.store, self.path):
227+
for key in sorted(listdir(self.store, self.path)):
243228
path = self.path + '/' + key
244229
if contains_array(self.store, path):
245230
yield key
246231

247232
def arrays(self):
248-
for key in listdir(self.store, self.path):
233+
for key in sorted(listdir(self.store, self.path)):
249234
path = self.path + '/' + key
250235
if contains_array(self.store, path):
251236
yield key, Array(self.store, path=path, readonly=self.readonly)
@@ -308,7 +293,7 @@ def create_dataset(self, name, data=None, shape=None, chunks=None,
308293
elif not contains_group(self.store, p):
309294
init_group(self.store, path=p)
310295

311-
# create terminal group
296+
# create array
312297
if contains_array(self.store, path):
313298
raise KeyError(name)
314299
if contains_group(self.store, path):

zarr/storage.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ def contains_group(store, path=None):
5050

5151

5252
def _rmdir_from_keys(store, path=None):
53-
# TODO review, esp. with None prefix
5453
# assume path already normalized
5554
prefix = _path_to_prefix(path)
5655
for key in set(store.keys()):
@@ -59,8 +58,7 @@ def _rmdir_from_keys(store, path=None):
5958

6059

6160
def rmdir(store, path=None):
62-
"""TODO"""
63-
# TODO review
61+
"""TODO doc me"""
6462
path = normalize_storage_path(path)
6563
if hasattr(store, 'rmdir'):
6664
# pass through
@@ -71,7 +69,6 @@ def rmdir(store, path=None):
7169

7270

7371
def _listdir_from_keys(store, path=None):
74-
# TODO review, esp. with None prefix
7572
# assume path already normalized
7673
prefix = _path_to_prefix(path)
7774
children = set()
@@ -84,8 +81,7 @@ def _listdir_from_keys(store, path=None):
8481

8582

8683
def listdir(store, path=None):
87-
"""TODO"""
88-
# TODO review
84+
"""TODO doc me"""
8985
path = normalize_storage_path(path)
9086
if hasattr(store, 'listdir'):
9187
# pass through

zarr/tests/test_hierarchy.py

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from zarr.core import Array
1919
from zarr.hierarchy import Group
2020
from zarr.attrs import Attributes
21+
from zarr.errors import ReadOnlyError
2122

2223

2324
# noinspection PyStatementEffect
@@ -187,6 +188,45 @@ def test_create_dataset(self):
187188
eq('/bar', d3.name)
188189
assert_is(store, d3.store)
189190

191+
def test_create_errors(self):
192+
store = self.create_store()
193+
init_group(store)
194+
195+
# array obstructs group, array
196+
g = Group(store=store)
197+
g.create_dataset('foo', shape=100, chunks=10)
198+
with assert_raises(KeyError):
199+
g.create_group('foo/bar')
200+
with assert_raises(KeyError):
201+
g.require_group('foo/bar')
202+
with assert_raises(KeyError):
203+
g.create_dataset('foo/bar', shape=100, chunks=10)
204+
205+
# array obstructs group, array
206+
g.create_dataset('a/b', shape=100, chunks=10)
207+
with assert_raises(KeyError):
208+
g.create_group('a/b')
209+
with assert_raises(KeyError):
210+
g.require_group('a/b')
211+
with assert_raises(KeyError):
212+
g.create_dataset('a/b', shape=100, chunks=10)
213+
214+
# group obstructs array
215+
g.create_group('c/d')
216+
with assert_raises(KeyError):
217+
g.create_dataset('c', shape=100, chunks=10)
218+
with assert_raises(KeyError):
219+
g.create_dataset('c/d', shape=100, chunks=10)
220+
221+
# read-only
222+
g = Group(store=store, readonly=True)
223+
with assert_raises(ReadOnlyError):
224+
g.create_group('zzz')
225+
with assert_raises(ReadOnlyError):
226+
g.require_group('zzz')
227+
with assert_raises(ReadOnlyError):
228+
g.create_dataset('zzz', shape=100, chunks=10)
229+
190230
def test_getitem_contains_iterators(self):
191231
# setup
192232
store = self.create_store()
@@ -243,18 +283,67 @@ def test_getitem_contains_iterators(self):
243283
eq(1, len(g1['a']))
244284
eq(1, len(g1['a/b']))
245285

246-
# test keys()
247-
eq(['a', 'foo'], sorted(g1.keys()))
248-
eq(['bar', 'baz'], sorted(g1['foo'].keys()))
286+
# test __iter__, keys()
287+
# currently assumes sorted by key
288+
289+
eq(['a', 'foo'], list(g1))
290+
eq(['a', 'foo'], list(g1.keys()))
291+
eq(['bar', 'baz'], list(g1['foo']))
292+
eq(['bar', 'baz'], list(g1['foo'].keys()))
293+
eq([], sorted(g1['foo/bar']))
249294
eq([], sorted(g1['foo/bar'].keys()))
250295

296+
# test items(), values()
297+
# currently assumes sorted by key
298+
299+
items = list(g1.items())
300+
values = list(g1.values())
301+
eq('a', items[0][0])
302+
eq(g1['a'], items[0][1])
303+
eq(g1['a'], values[0])
304+
eq('foo', items[1][0])
305+
eq(g1['foo'], items[1][1])
306+
eq(g1['foo'], values[1])
307+
308+
items = list(g1['foo'].items())
309+
values = list(g1['foo'].values())
310+
eq('bar', items[0][0])
311+
eq(g1['foo']['bar'], items[0][1])
312+
eq(g1['foo']['bar'], values[0])
313+
eq('baz', items[1][0])
314+
eq(g1['foo']['baz'], items[1][1])
315+
eq(g1['foo']['baz'], values[1])
316+
317+
# test array_keys(), arrays(), group_keys(), groups()
318+
# currently assumes sorted by key
319+
320+
eq(['a', 'foo'], list(g1.group_keys()))
321+
groups = list(g1.groups())
322+
arrays = list(g1.arrays())
323+
eq('a', groups[0][0])
324+
eq(g1['a'], groups[0][1])
325+
eq('foo', groups[1][0])
326+
eq(g1['foo'], groups[1][1])
327+
eq([], list(g1.array_keys()))
328+
eq([], arrays)
329+
330+
eq(['bar'], list(g1['foo'].group_keys()))
331+
eq(['baz'], list(g1['foo'].array_keys()))
332+
groups = list(g1['foo'].groups())
333+
arrays = list(g1['foo'].arrays())
334+
eq('bar', groups[0][0])
335+
eq(g1['foo']['bar'], groups[0][1])
336+
eq('baz', arrays[0][0])
337+
eq(g1['foo']['baz'], arrays[0][1])
338+
251339
def test_empty_getitem_contains_iterators(self):
252340
# setup
253341
store = self.create_store()
254342
init_group(store)
255343
g = Group(store=store)
256344

257345
# test
346+
eq([], list(g))
258347
eq([], list(g.keys()))
259348
eq(0, len(g))
260349
assert 'foo' not in g
@@ -263,9 +352,32 @@ def test_group_repr(self):
263352
store = self.create_store()
264353
init_group(store)
265354
g = Group(store=store)
266-
expect = 'zarr.hierarchy.Group(/, 0)\n store: builtins.dict'
355+
store_class = '%s.%s' % (dict.__module__, dict.__name__)
356+
expect = 'zarr.hierarchy.Group(/, 0)\n store: %s' % store_class
267357
actual = repr(g)
268358
eq(expect, actual)
359+
g.create_group('foo')
360+
g.create_group('bar')
361+
g.create_group('y'*80)
362+
g.create_dataset('baz', shape=100, chunks=10)
363+
g.create_dataset('quux', shape=100, chunks=10)
364+
g.create_dataset('z'*80, shape=100, chunks=10)
365+
expect = \
366+
'zarr.hierarchy.Group(/, 6)\n' \
367+
' arrays: 3; baz, quux, ' \
368+
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz...\n' \
369+
' groups: 3; bar, foo, ' \
370+
'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy...\n' \
371+
' store: %s' % store_class
372+
actual = repr(g)
373+
eq(expect, actual)
374+
375+
def test_setitem(self):
376+
store = self.create_store()
377+
init_group(store)
378+
g = Group(store=store)
379+
with assert_raises(NotImplementedError):
380+
g['foo'] = 'bar'
269381

270382

271383
class TestGroupDictStore(TestGroup):

0 commit comments

Comments
 (0)