Skip to content

Commit a9db264

Browse files
authored
Merge pull request #807 from effigies/fix/h5py_minver
FIX: Require h5py 2.10 for Windows + Python < 3.6
2 parents 22fe8c2 + 69700fe commit a9db264

11 files changed

+73
-18
lines changed

.azure-pipelines/windows.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
vmImage: ${{ parameters.vmImage }}
1111
variables:
1212
EXTRA_WHEELS: "https://5cf40426d9f06eb7461d-6fe47d9331aba7cd62fc36c7196769e4.ssl.cf2.rackcdn.com"
13+
DEPENDS: numpy scipy matplotlib h5py pydicom
1314
strategy:
1415
matrix:
1516
${{ insert }}: ${{ parameters.matrix }}
@@ -20,11 +21,14 @@ jobs:
2021
versionSpec: '$(PYTHON_VERSION)'
2122
addToPath: true
2223
architecture: '$(PYTHON_ARCH)'
24+
- script: |
25+
echo %PYTHONHASHSEED%
26+
displayName: 'Display hash seed'
2327
- script: |
2428
python -m pip install --upgrade pip setuptools>=30.3.0 wheel
2529
displayName: 'Update build tools'
2630
- script: |
27-
python -m pip install --find-links %EXTRA_WHEELS% numpy scipy matplotlib h5py pydicom
31+
python -m pip install --find-links %EXTRA_WHEELS% %DEPENDS%
2832
python -m pip install nose mock coverage codecov
2933
displayName: 'Install dependencies'
3034
- script: |

azure-pipelines.yml

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ jobs:
1111
py35-x64:
1212
PYTHON_VERSION: '3.5'
1313
PYTHON_ARCH: 'x64'
14+
py35-h5py-check:
15+
PYTHON_VERSION: '3.5'
16+
PYTHON_ARCH: 'x64'
17+
PYTHONHASHSEED: 283137131
18+
DEPENDS: "h5py==2.9.0"
1419
py36-x86:
1520
PYTHON_VERSION: '3.6'
1621
PYTHON_ARCH: 'x86'

nibabel/_h5py_compat.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import sys
2+
import os
3+
from .optpkg import optional_package
4+
5+
# PY35: A bug affected Windows installations of h5py in Python3 versions <3.6
6+
# due to random dictionary ordering, causing float64 data arrays to sometimes be
7+
# loaded as longdouble (also 64 bit on Windows). This caused stochastic failures
8+
# to correctly handle data caches, and possibly other subtle bugs we never
9+
# caught. This was fixed in h5py 2.10.
10+
# Please see https://github.com/nipy/nibabel/issues/665 for details.
11+
min_h5py = '2.10' if os.name == 'nt' and (3,) <= sys.version_info < (3, 6) else None
12+
h5py, have_h5py, setup_module = optional_package('h5py', min_version=min_h5py)

nibabel/minc1.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def _normalize(self, data, sliceobj=()):
173173
applied to `data`
174174
"""
175175
ddt = self.get_data_dtype()
176-
if ddt.type in np.sctypes['float']:
176+
if np.issubdtype(ddt.type, np.floating):
177177
return data
178178
image_max = self._image_max
179179
image_min = self._image_min

nibabel/minc2.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828
import numpy as np
2929

3030
from .keywordonly import kw_only_meth
31-
from .optpkg import optional_package
32-
h5py, have_h5py, setup_module = optional_package('h5py')
31+
from ._h5py_compat import h5py
3332

3433
from .minc1 import Minc1File, MincHeader, Minc1Image, MincError
3534

nibabel/tests/test_h5py_compat.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
These tests are almost certainly overkill, but serve to verify that
3+
the behavior of _h5py_compat is pass-through in all but a small set of
4+
well-defined cases
5+
"""
6+
import sys
7+
import os
8+
from distutils.version import LooseVersion
9+
import numpy as np
10+
11+
from ..optpkg import optional_package
12+
from .. import _h5py_compat as compat
13+
from ..testing import assert_equal, assert_true, assert_false, assert_not_equal
14+
15+
h5py, have_h5py, _ = optional_package('h5py')
16+
17+
18+
def test_optpkg_equivalence():
19+
# No effect on Linux/OSX
20+
if os.name == 'posix':
21+
assert_equal(have_h5py, compat.have_h5py)
22+
# No effect on Python 2.7 or 3.6+
23+
if sys.version_info >= (3, 6) or sys.version_info < (3,):
24+
assert_equal(have_h5py, compat.have_h5py)
25+
# Available in a strict subset of cases
26+
if not have_h5py:
27+
assert_false(compat.have_h5py)
28+
# Available when version is high enough
29+
elif LooseVersion(h5py.__version__) >= '2.10':
30+
assert_true(compat.have_h5py)
31+
32+
33+
def test_disabled_h5py_cases():
34+
# On mismatch
35+
if have_h5py and not compat.have_h5py:
36+
# Recapitulate min_h5py conditions from _h5py_compat
37+
assert_equal(os.name, 'nt')
38+
assert_true((3,) <= sys.version_info < (3, 6))
39+
assert_true(LooseVersion(h5py.__version__) < '2.10')
40+
# Verify that the root cause is present
41+
# If any tests fail, they will likely be these, so they may be
42+
# ill-advised...
43+
assert_equal(str(np.longdouble), str(np.float64))
44+
assert_not_equal(np.longdouble, np.float64)

nibabel/tests/test_image_api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
from ..optpkg import optional_package
3434
_, have_scipy, _ = optional_package('scipy')
35-
_, have_h5py, _ = optional_package('h5py')
35+
from .._h5py_compat import have_h5py
3636

3737
from .. import (AnalyzeImage, Spm99AnalyzeImage, Spm2AnalyzeImage,
3838
Nifti1Pair, Nifti1Image, Nifti2Pair, Nifti2Image,

nibabel/tests/test_imageclasses.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66

77
import numpy as np
88

9-
from nibabel.optpkg import optional_package
10-
119
import nibabel as nib
1210
from nibabel.analyze import AnalyzeImage
1311
from nibabel.nifti1 import Nifti1Image
1412
from nibabel.nifti2 import Nifti2Image
13+
from .._h5py_compat import have_h5py
1514

1615
from nibabel import imageclasses
1716
from nibabel.imageclasses import spatial_axes_first, class_map, ext_map
@@ -23,8 +22,6 @@
2322

2423
DATA_DIR = pjoin(dirname(__file__), 'data')
2524

26-
have_h5py = optional_package('h5py')[1]
27-
2825
MINC_3DS = ('minc1_1_scale.mnc',)
2926
MINC_4DS = ('minc1_4d.mnc',)
3027
if have_h5py:

nibabel/tests/test_minc2.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111

1212
import numpy as np
1313

14-
from ..optpkg import optional_package
15-
16-
h5py, have_h5py, setup_module = optional_package('h5py')
17-
1814
from .. import minc2
1915
from ..minc2 import Minc2File, Minc2Image
16+
from .._h5py_compat import h5py, have_h5py, setup_module
2017

2118
from nose.tools import (assert_true, assert_equal, assert_false, assert_raises)
2219

nibabel/tests/test_minc2_data.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414

1515
import numpy as np
1616

17-
from nibabel.optpkg import optional_package
18-
19-
h5py, have_h5py, setup_module = optional_package('h5py')
17+
from .._h5py_compat import h5py, have_h5py, setup_module
2018

2119
from .nibabel_data import get_nibabel_data, needs_nibabel_data
2220
from .. import load as top_load, Nifti1Image

nibabel/tests/test_proxy_api.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
from .. import minc1
4545
from ..externals.netcdf import netcdf_file
4646
from .. import minc2
47-
from ..optpkg import optional_package
48-
h5py, have_h5py, _ = optional_package('h5py')
47+
from .._h5py_compat import h5py, have_h5py
4948
from .. import ecat
5049
from .. import parrec
5150

0 commit comments

Comments
 (0)