Skip to content

Commit 64e16d2

Browse files
committed
refactor hierarchy module; all tests passing under py3
1 parent e5b6fb8 commit 64e16d2

File tree

10 files changed

+460
-327
lines changed

10 files changed

+460
-327
lines changed

zarr/core.py

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
from zarr.compressors import get_compressor_cls
1212
from zarr.util import is_total_slice, normalize_array_selection, \
13-
get_chunk_range, human_readable_size, normalize_resize_args
14-
from zarr.storage import normalize_prefix, normalize_key, array_meta_key, \
15-
array_attrs_key, listdir
16-
from zarr.meta import decode_metadata, encode_metadata
13+
get_chunk_range, human_readable_size, normalize_resize_args, \
14+
normalize_storage_path
15+
from zarr.storage import array_meta_key, attrs_key, listdir, contains_group, \
16+
buffersize
17+
from zarr.meta import decode_array_metadata, encode_array_metadata
1718
from zarr.attrs import Attributes
18-
from zarr.compat import itervalues
1919
from zarr.errors import ReadOnlyError
2020

2121

@@ -70,22 +70,30 @@ class Array(object):
7070
7171
""" # flake8: noqa
7272

73-
def __init__(self, store, name=None, readonly=False):
73+
def __init__(self, store, path=None, readonly=False):
7474
# N.B., expect at this point store is fully initialised with all
7575
# configuration metadata fully specified and normalised
7676

7777
self._store = store
78-
self._prefix = normalize_prefix(name)
78+
self._path = normalize_storage_path(path)
79+
if self._path:
80+
self._key_prefix = self._path + '/'
81+
else:
82+
self._key_prefix = ''
7983
self._readonly = readonly
8084

85+
# guard conditions
86+
if contains_group(store, path=self._path):
87+
raise ValueError('store contains a group')
88+
8189
# initialise metadata
82-
meta_key = array_meta_key(self._prefix)
8390
try:
84-
meta_bytes = store[meta_key]
91+
mkey = self._key_prefix + array_meta_key
92+
meta_bytes = store[mkey]
8593
except KeyError:
8694
raise ValueError('store has no metadata')
8795
else:
88-
meta = decode_metadata(meta_bytes)
96+
meta = decode_array_metadata(meta_bytes)
8997
self._meta = meta
9098
self._shape = meta['shape']
9199
self._chunks = meta['chunks']
@@ -98,28 +106,36 @@ def __init__(self, store, name=None, readonly=False):
98106
self._compressor = compressor_cls(self._compression_opts)
99107

100108
# initialise attributes
101-
attrs_key = array_attrs_key(self._prefix)
102-
self._attrs = Attributes(store, key=attrs_key, readonly=readonly)
109+
akey = self._key_prefix + attrs_key
110+
self._attrs = Attributes(store, key=akey, readonly=readonly)
103111

104112
def flush_metadata(self):
105113
meta = dict(shape=self._shape, chunks=self._chunks, dtype=self._dtype,
106114
compression=self._compression,
107115
compression_opts=self._compression_opts,
108116
fill_value=self._fill_value, order=self._order)
109-
meta_key = array_meta_key(self._prefix)
110-
self._store[meta_key] = encode_metadata(meta)
117+
mkey = self._key_prefix + array_meta_key
118+
self._store[mkey] = encode_array_metadata(meta)
111119

112120
@property
113121
def store(self):
114122
"""A MutableMapping providing the underlying storage for the array."""
115123
return self._store
116124

125+
@property
126+
def path(self):
127+
"""TODO doc me"""
128+
return self._path
129+
117130
@property
118131
def name(self):
119-
"""TODO"""
120-
if self._prefix:
121-
# follow h5py convention: add leading slash, remove trailing slash
122-
return '/' + self._prefix[:-1]
132+
"""TODO doc me"""
133+
if self.path:
134+
# follow h5py convention: add leading slash
135+
name = self.path
136+
if name[0] != '/':
137+
name = '/' + name
138+
return name
123139
return None
124140

125141
@property
@@ -196,29 +212,25 @@ def nbytes_stored(self):
196212
attributes encoded as JSON."""
197213
if hasattr(self._store, 'getsize'):
198214
# pass through
199-
return self._store.getsize(self._prefix)
215+
return self._store.getsize(self._path)
200216
elif isinstance(self._store, dict):
201-
# cheap to compute by summing length of values
217+
# compute from size of values
202218
size = 0
203-
for child in ls(self._store, self._prefix):
204-
key = self._prefix + child
219+
for k in listdir(self._store, self._path):
220+
v = self._store[self._key_prefix + k]
205221
try:
206-
size += len(self._store[key])
207-
except KeyError:
208-
pass
222+
size += buffersize(v)
223+
except TypeError:
224+
return -1
209225
return size
210226
else:
211227
return -1
212228

213229
@property
214230
def initialized(self):
215231
"""The number of chunks that have been initialized with some data."""
216-
n = 0
217-
for child in listdir(self._store, self._prefix):
218-
key = self._prefix + child
219-
if key in self._store:
220-
n += 1
221-
# N.B., expect 'meta' and 'attrs' keys in store also, so subtract 2
232+
n = sum(1 for _ in listdir(self._store, self._path))
233+
# N.B., expect meta and attrs keys in store also, so subtract 2
222234
return n - 2
223235

224236
@property
@@ -234,7 +246,7 @@ def __eq__(self, other):
234246
isinstance(other, Array) and
235247
self.store == other.store and
236248
self.readonly == other.readonly and
237-
self.name == other.name
249+
self.path == other.path
238250
# N.B., no need to compare other properties, should be covered by
239251
# store comparison
240252
)
@@ -509,7 +521,7 @@ def _chunk_getitem(self, cidx, item, dest):
509521
try:
510522

511523
# obtain compressed data for chunk
512-
ckey = self._prefix + '.'.join(map(str, cidx))
524+
ckey = self._ckey(cidx)
513525
cdata = self._store[ckey]
514526

515527
except KeyError:
@@ -584,7 +596,7 @@ def _chunk_setitem(self, cidx, key, value):
584596
try:
585597

586598
# obtain compressed data for chunk
587-
ckey = self._prefix + '.'.join(map(str, cidx))
599+
ckey = self._ckey(cidx)
588600
cdata = self._store[ckey]
589601

590602
except KeyError:
@@ -609,9 +621,12 @@ def _chunk_setitem(self, cidx, key, value):
609621
cdata = self._compressor.compress(chunk)
610622

611623
# store
612-
ckey = '.'.join(map(str, cidx))
624+
ckey = self._ckey(cidx)
613625
self._store[ckey] = cdata
614626

627+
def _ckey(self, cidx):
628+
return self._key_prefix + '.'.join(map(str, cidx))
629+
615630
def __repr__(self):
616631
r = '%s.%s(' % (type(self).__module__, type(self).__name__)
617632
if self.name:
@@ -635,7 +650,7 @@ def __repr__(self):
635650
return r
636651

637652
def __getstate__(self):
638-
return self._store, self._prefix, self._readonly
653+
return self._store, self._path, self._readonly
639654

640655
def __setstate__(self, state):
641656
self.__init__(*state)
@@ -690,7 +705,7 @@ def resize(self, *args):
690705

691706
# remove any chunks not within range
692707
for key in list(self._store):
693-
if key not in ['meta', 'attrs']:
708+
if key not in [array_meta_key, attrs_key]:
694709
cidx = map(int, key.split('.'))
695710
if all(i < c for i, c in zip(cidx, new_cdata_shape)):
696711
pass # keep the chunk

0 commit comments

Comments
 (0)