13
13
from zarr .storage import array_meta_key , attrs_key , listdir , getsize
14
14
from zarr .meta import decode_array_metadata , encode_array_metadata
15
15
from zarr .attrs import Attributes
16
- from zarr .errors import PermissionError
16
+ from zarr .errors import PermissionError , err_read_only , err_array_not_found
17
17
from zarr .compat import reduce
18
18
from zarr .codecs import get_codec
19
19
@@ -34,6 +34,11 @@ class Array(object):
34
34
for storage of both chunks and metadata.
35
35
synchronizer : object, optional
36
36
Array synchronizer.
37
+ cache_metadata : bool, optional
38
+ If True, array configuration metadata will be cached for the
39
+ lifetime of the object. If False, array metadata will be reloaded
40
+ prior to all data access and modification operations (may incur
41
+ overhead depending on storage and data access pattern).
37
42
38
43
Attributes
39
44
----------
@@ -56,8 +61,9 @@ class Array(object):
56
61
itemsize
57
62
nbytes
58
63
nbytes_stored
59
- initialized
60
64
cdata_shape
65
+ nchunks
66
+ nchunks_initialized
61
67
is_view
62
68
63
69
Methods
@@ -71,7 +77,7 @@ class Array(object):
71
77
""" # flake8: noqa
72
78
73
79
def __init__ (self , store , path = None , read_only = False , chunk_store = None ,
74
- synchronizer = None ):
80
+ synchronizer = None , cache_metadata = True ):
75
81
# N.B., expect at this point store is fully initialized with all
76
82
# configuration metadata fully specified and normalized
77
83
@@ -87,13 +93,23 @@ def __init__(self, store, path=None, read_only=False, chunk_store=None,
87
93
else :
88
94
self ._chunk_store = chunk_store
89
95
self ._synchronizer = synchronizer
96
+ self ._cache_metadata = cache_metadata
97
+ self ._is_view = False
90
98
91
99
# initialize metadata
100
+ self ._load_metadata ()
101
+
102
+ # initialize attributes
103
+ akey = self ._key_prefix + attrs_key
104
+ self ._attrs = Attributes (store , key = akey , read_only = read_only ,
105
+ synchronizer = synchronizer )
106
+
107
+ def _load_metadata (self ):
92
108
try :
93
109
mkey = self ._key_prefix + array_meta_key
94
- meta_bytes = store [mkey ]
110
+ meta_bytes = self . _store [mkey ]
95
111
except KeyError :
96
- raise ValueError ( 'store has no metadata' )
112
+ err_array_not_found ( self . _path )
97
113
else :
98
114
99
115
# decode and store metadata
@@ -104,7 +120,6 @@ def __init__(self, store, path=None, read_only=False, chunk_store=None,
104
120
self ._dtype = meta ['dtype' ]
105
121
self ._fill_value = meta ['fill_value' ]
106
122
self ._order = meta ['order' ]
107
- self ._is_view = False
108
123
109
124
# setup compressor
110
125
config = meta ['compressor' ]
@@ -119,14 +134,10 @@ def __init__(self, store, path=None, read_only=False, chunk_store=None,
119
134
filters = [get_codec (f ) for f in filters ]
120
135
self ._filters = filters
121
136
122
- # initialize attributes
123
- akey = self ._key_prefix + attrs_key
124
- self ._attrs = Attributes (store , key = akey , read_only = read_only ,
125
- synchronizer = synchronizer )
126
-
127
137
def _flush_metadata (self ):
128
138
if self ._is_view :
129
- raise PermissionError ('operation not permitted for views' )
139
+ raise PermissionError ('not permitted for views' )
140
+
130
141
if self ._compressor :
131
142
compressor_config = self ._compressor .get_config ()
132
143
else :
@@ -253,12 +264,6 @@ def nbytes_stored(self):
253
264
else :
254
265
return m + n
255
266
256
- @property
257
- def initialized (self ):
258
- """The number of chunks that have been initialized with some data."""
259
- return sum (1 for k in listdir (self ._chunk_store , self ._path )
260
- if k not in [array_meta_key , attrs_key ])
261
-
262
267
@property
263
268
def cdata_shape (self ):
264
269
"""A tuple of integers describing the number of chunks along each
@@ -267,6 +272,20 @@ def cdata_shape(self):
267
272
int (np .ceil (s / c )) for s , c in zip (self ._shape , self ._chunks )
268
273
)
269
274
275
+ @property
276
+ def nchunks (self ):
277
+ """Total number of chunks."""
278
+ return reduce (operator .mul , self .cdata_shape )
279
+
280
+ @property
281
+ def nchunks_initialized (self ):
282
+ """The number of chunks that have been initialized with some data."""
283
+ return sum (1 for k in listdir (self ._chunk_store , self ._path )
284
+ if k not in [array_meta_key , attrs_key ])
285
+
286
+ # backwards compability
287
+ initialized = nchunks_initialized
288
+
270
289
@property
271
290
def is_view (self ):
272
291
"""A boolean, True if this array is a view on another array."""
@@ -366,6 +385,10 @@ def __getitem__(self, item):
366
385
367
386
""" # flake8: noqa
368
387
388
+ # refresh metadata
389
+ if not self ._cache_metadata :
390
+ self ._load_metadata ()
391
+
369
392
# normalize selection
370
393
selection = normalize_array_selection (item , self ._shape )
371
394
@@ -482,7 +505,11 @@ def __setitem__(self, key, value):
482
505
483
506
# guard conditions
484
507
if self ._read_only :
485
- raise PermissionError ('array is read-only' )
508
+ err_read_only ()
509
+
510
+ # refresh metadata
511
+ if not self ._cache_metadata :
512
+ self ._load_metadata ()
486
513
487
514
# normalize selection
488
515
selection = normalize_array_selection (key , self ._shape )
@@ -717,6 +744,10 @@ def _encode_chunk(self, chunk):
717
744
718
745
def __repr__ (self ):
719
746
747
+ # refresh metadata
748
+ if not self ._cache_metadata :
749
+ self ._load_metadata ()
750
+
720
751
# main line
721
752
r = '%s(' % type (self ).__name__
722
753
if self .name :
@@ -733,8 +764,8 @@ def __repr__(self):
733
764
r += '; nbytes_stored: %s' % human_readable_size (
734
765
self .nbytes_stored )
735
766
r += '; ratio: %.1f' % (self .nbytes / self .nbytes_stored )
736
- n_chunks = reduce ( operator . mul , self .cdata_shape )
737
- r += '; initialized: %s/%s' % ( self .initialized , n_chunks )
767
+ r += '; initialized: %s/%s' % ( self .nchunks_initialized ,
768
+ self .nchunks )
738
769
739
770
# filters
740
771
if self .filters :
@@ -768,15 +799,28 @@ def _write_op(self, f, *args, **kwargs):
768
799
769
800
# guard condition
770
801
if self ._read_only :
771
- raise PermissionError ( 'array is read-only' )
802
+ err_read_only ( )
772
803
773
804
# synchronization
774
805
if self ._synchronizer is None :
806
+
807
+ # refresh metadata
808
+ if not self ._cache_metadata :
809
+ self ._load_metadata ()
810
+
775
811
return f (* args , ** kwargs )
812
+
776
813
else :
814
+
777
815
# synchronize on the array
778
816
mkey = self ._key_prefix + array_meta_key
817
+
779
818
with self ._synchronizer [mkey ]:
819
+
820
+ # refresh metadata
821
+ if not self ._cache_metadata :
822
+ self ._load_metadata ()
823
+
780
824
return f (* args , ** kwargs )
781
825
782
826
def resize (self , * args ):
@@ -1022,7 +1066,7 @@ def view(self, shape=None, chunks=None, dtype=None,
1022
1066
... v.resize(20000)
1023
1067
... except PermissionError as e:
1024
1068
... print(e)
1025
- operation not permitted for views
1069
+ not permitted for views
1026
1070
1027
1071
""" # flake8: noqa
1028
1072
@@ -1034,7 +1078,8 @@ def view(self, shape=None, chunks=None, dtype=None,
1034
1078
if synchronizer is None :
1035
1079
synchronizer = self ._synchronizer
1036
1080
a = Array (store = store , path = path , chunk_store = chunk_store ,
1037
- read_only = read_only , synchronizer = synchronizer )
1081
+ read_only = read_only , synchronizer = synchronizer ,
1082
+ cache_metadata = True )
1038
1083
a ._is_view = True
1039
1084
1040
1085
# allow override of some properties
0 commit comments