Skip to content

Commit 07fa81a

Browse files
authored
Merge pull request #239 from zarr-developers/npy114-20180130-alimanfoo
Work around numpy 1.14 structured array changes
2 parents c816ab3 + ccdac39 commit 07fa81a

File tree

9 files changed

+72
-40
lines changed

9 files changed

+72
-40
lines changed

appveyor.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,45 @@ environment:
1414

1515
- PYTHON: "C:\\Python27"
1616
PYTHON_VERSION: "2.7"
17+
NUMPY_VERSION: "1.13.3"
1718

1819
- PYTHON: "C:\\Python27-x64"
1920
PYTHON_VERSION: "2.7"
21+
NUMPY_VERSION: "1.13.3"
2022
DISTUTILS_USE_SDK: "1"
2123

2224
- PYTHON: "C:\\Python34"
25+
NUMPY_VERSION: "1.13.3"
2326
PYTHON_VERSION: "3.4"
2427

2528
- PYTHON: "C:\\Python34-x64"
2629
PYTHON_VERSION: "3.4"
30+
NUMPY_VERSION: "1.13.3"
2731
DISTUTILS_USE_SDK: "1"
2832

2933
- PYTHON: "C:\\Python35"
3034
PYTHON_VERSION: "3.5"
35+
NUMPY_VERSION: "1.13.3"
3136

3237
- PYTHON: "C:\\Python35-x64"
3338
PYTHON_VERSION: "3.5"
39+
NUMPY_VERSION: "1.13.3"
3440

3541
- PYTHON: "C:\\Python36"
3642
PYTHON_VERSION: "3.6"
43+
NUMPY_VERSION: "1.13.3"
3744

3845
- PYTHON: "C:\\Python36-x64"
3946
PYTHON_VERSION: "3.6"
47+
NUMPY_VERSION: "1.13.3"
48+
49+
- PYTHON: "C:\\Python36"
50+
PYTHON_VERSION: "3.6"
51+
NUMPY_VERSION: "1.14.0"
52+
53+
- PYTHON: "C:\\Python36-x64"
54+
PYTHON_VERSION: "3.6"
55+
NUMPY_VERSION: "1.14.0"
4056

4157
install:
4258
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
@@ -45,6 +61,8 @@ build: off
4561

4662
test_script:
4763
- "%CMD_IN_ENV% python -m pip install -U pip setuptools wheel"
64+
- "%CMD_IN_ENV% python -m pip install numpy==%NUMPY_VERSION%"
4865
- "%CMD_IN_ENV% python -m pip install -rrequirements_dev.txt"
49-
- "%CMD_IN_ENV% python setup.py bdist_wheel"
50-
- "%CMD_IN_ENV% python -m pytest -v zarr"
66+
- "%CMD_IN_ENV% python setup.py install"
67+
- "%CMD_IN_ENV% python -m pytest -v --pyargs zarr"
68+

docs/release.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Release notes
66
2.2.0 (release candidate)
77
-------------------------
88

9-
Version 2.2.0 is currently at the release candidate stage. To install the latest release
9+
Version 2.2.0 is currently at the release candidate stage. To install the latest release
1010
candidate version::
1111

1212
$ pip install --pre zarr
@@ -190,9 +190,12 @@ Maintenance
190190

191191
* A data fixture has been included in the test suite to ensure data format
192192
compatibility is maintained; :issue:`83`, :issue:`146`.
193-
* The test suite has been migrated from nosetests to pytest; :issue:`189`, :issue:`225`, :issue:`237`.
193+
* The test suite has been migrated from nosetests to pytest; :issue:`189`, :issue:`225`.
194194
* Various continuous integration updates and improvements; :issue:`118`, :issue:`124`,
195195
:issue:`125`, :issue:`126`, :issue:`109`, :issue:`114`, :issue:`171`.
196+
* Bump numcodecs dependency to 0.5.3, completely remove nose dependency, :issue:`237`.
197+
* Fix compatibility issues with NumPy 1.14 regarding fill values for structured arrays,
198+
:issue:`222`, :issue:`238`, :issue:`239`.
196199

197200
Acknowledgments
198201
~~~~~~~~~~~~~~~

requirements_dev.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ mccabe==0.6.1
1616
monotonic==1.3
1717
msgpack-python==0.4.8
1818
numcodecs==0.5.3
19-
numpy==1.13.3
2019
packaging==16.8
2120
pkginfo==1.4.1
2221
pluggy==0.5.2

tox.ini

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# and then run "tox" from this directory.
55

66
[tox]
7-
envlist = py27, py34, py35, py36, docs
7+
envlist = py27, py34, py35, py36-npy{113,114}, docs
88

99
[testenv]
1010
setenv =
@@ -15,17 +15,20 @@ setenv =
1515
commands =
1616
python -c 'import glob; import shutil; import os; [(shutil.rmtree(d) if os.path.isdir(d) else os.remove(d) if os.path.isfile(d) else None) for d in glob.glob("./example*")]'
1717
py27,py34,py35: pytest -v --cov=zarr zarr
18-
py36: pytest -v --cov=zarr --doctest-modules zarr
19-
coverage report -m
20-
py36: python -m doctest -o NORMALIZE_WHITESPACE -o ELLIPSIS docs/tutorial.rst docs/spec/v2.rst
21-
py36: flake8 --max-line-length=100 zarr
18+
# don't run py36-npy114 with coverage because it is run together with py35-npy113 on travis
19+
py36-npy114: pytest -v zarr
20+
py36-npy113: pytest -v --cov=zarr --doctest-modules zarr
21+
py27,py34,py35,py36-npy113: coverage report -m
22+
py36-npy113: python -m doctest -o NORMALIZE_WHITESPACE -o ELLIPSIS docs/tutorial.rst docs/spec/v2.rst
23+
py36-npy113: flake8 --max-line-length=100 zarr
2224
deps =
2325
py27: backports.lzma
26+
py27,py34,py35,py36-npy113: numpy==1.13.3
27+
py36-npy114: numpy==1.14.0
2428
-rrequirements_dev.txt
2529
# linux only
2630
-rrequirements_dev_optional.txt
2731

28-
2932
[testenv:docs]
3033
basepython = python2.7
3134
changedir = docs

zarr/meta.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,20 @@ def decode_fill_value(v, dtype):
138138
return np.NINF
139139
else:
140140
return np.array(v, dtype=dtype)[()]
141-
elif dtype.kind in 'SV':
141+
elif dtype.kind == 'S':
142142
# noinspection PyBroadException
143143
try:
144144
v = base64.standard_b64decode(v)
145-
v = np.array(v, dtype=dtype)[()]
146-
return v
147145
except Exception:
148146
# be lenient, allow for other values that may have been used before base64
149147
# encoding and may work as fill values, e.g., the number 0
150-
return v
148+
pass
149+
v = np.array(v, dtype=dtype)[()]
150+
return v
151+
elif dtype.kind == 'V':
152+
v = base64.standard_b64decode(v)
153+
v = np.array(v, dtype=dtype.str).view(dtype)[()]
154+
return v
151155
elif dtype.kind == 'U':
152156
# leave as-is
153157
return v
@@ -174,7 +178,7 @@ def encode_fill_value(v, dtype):
174178
return bool(v)
175179
elif dtype.kind in 'SV':
176180
v = base64.standard_b64encode(v)
177-
if not PY2:
181+
if not PY2: # pragma: py2 no cover
178182
v = str(v, 'ascii')
179183
return v
180184
elif dtype.kind == 'U':

zarr/tests/test_core.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -834,12 +834,15 @@ def test_structured_array(self):
834834
(b'ccc', 3, 12.6)],
835835
dtype=[('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')])
836836
for a in (d, d[:0]):
837-
for fill_value in None, b'', (b'zzz', 0, 0.0):
838-
z = self.create_array(shape=a.shape, chunks=2, dtype=a.dtype,
839-
fill_value=fill_value)
837+
for fill_value in None, b'', (b'zzz', 42, 16.8):
838+
z = self.create_array(shape=a.shape, chunks=2, dtype=a.dtype, fill_value=fill_value)
840839
assert len(a) == len(z)
841840
if fill_value is not None:
842-
np_fill_value = np.array(fill_value, dtype=a.dtype)[()]
841+
if fill_value == b'':
842+
# numpy 1.14 compatibility
843+
np_fill_value = np.array(fill_value, dtype=a.dtype.str).view(a.dtype)[()]
844+
else:
845+
np_fill_value = np.array(fill_value, dtype=a.dtype)[()]
843846
assert np_fill_value == z.fill_value
844847
if len(z):
845848
assert np_fill_value == z[0]
@@ -852,10 +855,6 @@ def test_structured_array(self):
852855
assert_array_equal(a['bar'], z['bar'])
853856
assert_array_equal(a['baz'], z['baz'])
854857

855-
with pytest.raises(ValueError):
856-
# dodgy fill value
857-
self.create_array(shape=a.shape, chunks=2, dtype=a.dtype, fill_value=42)
858-
859858
def test_dtypes(self):
860859

861860
# integers

zarr/tests/test_meta.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,14 @@ def test_encode_decode_array_2():
6969
# some variations
7070
df = Delta(astype='u2', dtype='V14')
7171
compressor = Blosc(cname='lz4', clevel=3, shuffle=2)
72+
dtype = np.dtype([('a', 'i4'), ('b', 'S10')])
73+
fill_value = np.zeros((), dtype=dtype)[()]
7274
meta = dict(
7375
shape=(100, 100),
7476
chunks=(10, 10),
75-
dtype=np.dtype([('a', 'i4'), ('b', 'S10')]),
77+
dtype=dtype,
7678
compressor=compressor.get_config(),
77-
fill_value=b'',
79+
fill_value=fill_value,
7880
order='F',
7981
filters=[df.get_config()]
8082
)
@@ -89,7 +91,7 @@ def test_encode_decode_array_2():
8991
"blocksize": 0
9092
},
9193
"dtype": [["a", "<i4"], ["b", "|S10"]],
92-
"fill_value": "",
94+
"fill_value": "AAAAAAAAAAAAAAAAAAA=",
9395
"filters": [
9496
{"id": "delta", "astype": "<u2", "dtype": "|V14"}
9597
],
@@ -110,8 +112,7 @@ def test_encode_decode_array_2():
110112
assert meta['dtype'] == meta_dec['dtype']
111113
assert meta['compressor'] == meta_dec['compressor']
112114
assert meta['order'] == meta_dec['order']
113-
np_fill_value = np.array(meta['fill_value'], dtype=meta['dtype'])[()]
114-
assert np_fill_value == meta_dec['fill_value']
115+
assert fill_value == meta_dec['fill_value']
115116
assert [df.get_config()] == meta_dec['filters']
116117

117118

zarr/tests/test_util.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ def test_normalize_order():
104104

105105
def test_normalize_fill_value():
106106
assert b'' == normalize_fill_value(0, dtype=np.dtype('S1'))
107-
assert b'' == normalize_fill_value(0, dtype=np.dtype([('foo', 'i4'), ('bar', 'f8')]))
107+
structured_dtype = np.dtype([('foo', 'S3'), ('bar', 'i4'), ('baz', 'f8')])
108+
expect = np.array((b'', 0, 0.), dtype=structured_dtype)[()]
109+
assert expect == normalize_fill_value(0, dtype=structured_dtype)
108110
assert '' == normalize_fill_value(0, dtype=np.dtype('U1'))
109111

110112

zarr/util.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -235,19 +235,16 @@ def normalize_fill_value(fill_value, dtype):
235235
# no fill value
236236
pass
237237

238-
elif fill_value == 0 and dtype.kind in 'SV':
239-
# special case because 0 used as default, but cannot be used for structured arrays
240-
fill_value = b''
238+
elif fill_value == 0:
239+
# this should be compatible across numpy versions for any array type, including
240+
# structured arrays
241+
fill_value = np.zeros((), dtype=dtype)[()]
241242

242243
elif dtype.kind == 'U':
243-
# special case unicode because of encoding issues on Windows if passed through
244-
# numpy
244+
# special case unicode because of encoding issues on Windows if passed through numpy
245245
# https://github.com/alimanfoo/zarr/pull/172#issuecomment-343782713
246246

247-
if fill_value == 0:
248-
fill_value = ''
249-
250-
elif PY2 and isinstance(fill_value, binary_type): # pragma: py3 no cover
247+
if PY2 and isinstance(fill_value, binary_type): # pragma: py3 no cover
251248
# this is OK on PY2, can be written as JSON
252249
pass
253250

@@ -257,11 +254,17 @@ def normalize_fill_value(fill_value, dtype):
257254

258255
else:
259256
try:
260-
fill_value = np.array(fill_value, dtype=dtype)[()]
257+
if isinstance(fill_value, binary_type) and dtype.kind == 'V':
258+
# special case for numpy 1.14 compatibility
259+
fill_value = np.array(fill_value, dtype=dtype.str).view(dtype)[()]
260+
else:
261+
fill_value = np.array(fill_value, dtype=dtype)[()]
262+
261263
except Exception as e:
262264
# re-raise with our own error message to be helpful
263265
raise ValueError('fill_value {!r} is not valid for dtype {}; nested '
264266
'exception: {}'.format(fill_value, dtype, e))
267+
265268
return fill_value
266269

267270

0 commit comments

Comments
 (0)