Skip to content

Commit 433bf72

Browse files
authored
Merge pull request #176 from alimanfoo/fill-bytes-20171031
Test and fix for bytes fill value bug
2 parents a05629e + d801a31 commit 433bf72

4 files changed

Lines changed: 85 additions & 11 deletions

File tree

docs/tutorial.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ Diagnostic information about arrays and groups is available via the ``info`` pro
462462
Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
463463
Store type : zarr.storage.DictStore
464464
No. bytes : 8000000 (7.6M)
465-
No. bytes stored : 38482 (37.6K)
465+
No. bytes stored : 38484 (37.6K)
466466
Storage ratio : 207.9
467467
Chunks initialized : 10/10
468468

zarr/creation.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,13 @@ def create(shape, chunks=None, dtype=None, compressor='default',
100100
# handle polymorphic store arg
101101
store = _handle_store_arg(store)
102102

103-
# compatibility
103+
# API compatibility with h5py
104104
compressor, fill_value = _handle_kwargs(compressor, fill_value, kwargs)
105105

106+
# ensure fill_value of correct type
107+
if fill_value is not None:
108+
fill_value = np.array(fill_value, dtype=dtype)[()]
109+
106110
# initialize array metadata
107111
init_array(store, shape=shape, chunks=chunks, dtype=dtype,
108112
compressor=compressor, fill_value=fill_value, order=order,
@@ -401,9 +405,13 @@ def open_array(store=None, mode='a', shape=None, chunks=None, dtype=None,
401405
store = _handle_store_arg(store)
402406
path = normalize_storage_path(path)
403407

404-
# compatibility
408+
# API compatibility with h5py
405409
compressor, fill_value = _handle_kwargs(compressor, fill_value, kwargs)
406410

411+
# ensure fill_value of correct type
412+
if fill_value is not None:
413+
fill_value = np.array(fill_value, dtype=dtype)[()]
414+
407415
# ensure store is initialized
408416

409417
if mode in ['r', 'r+']:

zarr/meta.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import, print_function, division
33
import json
4+
import base64
45

56

67
import numpy as np
@@ -40,13 +41,14 @@ def decode_array_metadata(s):
4041

4142

4243
def encode_array_metadata(meta):
44+
dtype = meta['dtype']
4345
meta = dict(
4446
zarr_format=ZARR_FORMAT,
4547
shape=meta['shape'],
4648
chunks=meta['chunks'],
47-
dtype=encode_dtype(meta['dtype']),
49+
dtype=encode_dtype(dtype),
4850
compressor=meta['compressor'],
49-
fill_value=encode_fill_value(meta['fill_value']),
51+
fill_value=encode_fill_value(meta['fill_value'], dtype),
5052
order=meta['order'],
5153
filters=meta['filters'],
5254
)
@@ -110,6 +112,9 @@ def encode_group_metadata(meta=None):
110112

111113

112114
def decode_fill_value(v, dtype):
115+
# early out
116+
if v is None:
117+
return v
113118
if dtype.kind == 'f':
114119
if v == 'NaN':
115120
return np.nan
@@ -118,20 +123,39 @@ def decode_fill_value(v, dtype):
118123
elif v == '-Infinity':
119124
return np.NINF
120125
else:
126+
return np.array(v, dtype=dtype)[()]
127+
elif dtype.kind == 'S':
128+
try:
129+
return base64.standard_b64decode(v)
130+
except Exception:
131+
# be lenient, allow for other values that may have been used before base64 encoding
132+
# and may work as fill values, e.g., the number 0
121133
return v
122134
else:
123135
return v
124136

125137

126-
def encode_fill_value(v):
127-
try:
138+
def encode_fill_value(v, dtype):
139+
# early out
140+
if v is None:
141+
return v
142+
if dtype.kind == 'f':
128143
if np.isnan(v):
129144
return 'NaN'
130145
elif np.isposinf(v):
131146
return 'Infinity'
132147
elif np.isneginf(v):
133148
return '-Infinity'
134149
else:
135-
return v
136-
except TypeError:
150+
return float(v)
151+
elif dtype.kind in 'ui':
152+
return int(v)
153+
elif dtype.kind == 'b':
154+
return bool(v)
155+
elif dtype.kind == 'S':
156+
v = base64.standard_b64encode(v)
157+
if not PY2:
158+
v = str(v, 'ascii')
159+
return v
160+
else:
137161
return v

zarr/tests/test_meta.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import, print_function, division
33
import json
4+
import base64
45

56

67
from nose.tools import eq_ as eq, assert_is_none, assert_raises
78
import numpy as np
89

910

10-
from zarr.compat import binary_type, text_type
11+
from zarr.compat import binary_type, text_type, PY2
1112
from zarr.meta import decode_array_metadata, encode_dtype, decode_dtype, \
1213
ZARR_FORMAT, decode_group_metadata, encode_array_metadata
1314
from zarr.errors import MetadataError
@@ -113,7 +114,7 @@ def test_encode_decode_array_2():
113114
eq([df.get_config()], meta_dec['filters'])
114115

115116

116-
def test_encode_decode_array_fill_values():
117+
def test_encode_decode_fill_values_nan():
117118

118119
fills = (
119120
(np.nan, "NaN", np.isnan),
@@ -154,6 +155,47 @@ def test_encode_decode_array_fill_values():
154155
assert f(actual)
155156

156157

158+
def test_encode_decode_fill_values_bytes():
159+
160+
fills = b'foo', bytes(10)
161+
162+
for v in fills:
163+
164+
s = base64.standard_b64encode(v)
165+
if not PY2:
166+
s = str(s, 'ascii')
167+
168+
meta = dict(
169+
shape=(100,),
170+
chunks=(10,),
171+
dtype=np.dtype('S10'),
172+
compressor=Zlib(1).get_config(),
173+
fill_value=v,
174+
filters=None,
175+
order='C'
176+
)
177+
178+
meta_json = '''{
179+
"chunks": [10],
180+
"compressor": {"id": "zlib", "level": 1},
181+
"dtype": "|S10",
182+
"fill_value": "%s",
183+
"filters": null,
184+
"order": "C",
185+
"shape": [100],
186+
"zarr_format": %s
187+
}''' % (s, ZARR_FORMAT)
188+
189+
# test encoding
190+
meta_enc = encode_array_metadata(meta)
191+
assert_json_eq(meta_json, meta_enc)
192+
193+
# test decoding
194+
meta_dec = decode_array_metadata(meta_enc)
195+
actual = meta_dec['fill_value']
196+
eq(v, actual)
197+
198+
157199
def test_decode_array_unsupported_format():
158200

159201
# unsupported format

0 commit comments

Comments
 (0)