Skip to content

Commit a13de6e

Browse files
authored
Merge pull request #700 from effigies/fix/writable_bug
FIX: Numpy pre-release accommodations
2 parents 3652f16 + ea1b0cc commit a13de6e

16 files changed

+57
-55
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ before_install:
9696
- virtualenv --python=python venv
9797
- source venv/bin/activate
9898
- python --version # just to check
99-
- pip install -U pip wheel # needed at one point
99+
- pip install -U pip setuptools>=27.0 wheel
100100
- retry pip install nose flake8 mock # always
101101
- pip install $EXTRA_PIP_FLAGS $DEPENDS $OPTIONAL_DEPENDS
102102
- if [ "${COVERAGE}" == "1" ]; then

appveyor.yml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ install:
2222
- SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%
2323

2424
# Install the dependencies of the project.
25+
- python -m pip install --upgrade pip setuptools wheel
2526
- pip install numpy scipy matplotlib nose h5py mock pydicom
2627
- pip install .
2728
- SET NIBABEL_DATA_DIR=%CD%\nibabel-data

nibabel/cifti2/tests/test_cifti2.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_cifti2_metadata():
5858
assert_equal(md.data, dict(metadata_test))
5959

6060
assert_equal(list(iter(md)), list(iter(collections.OrderedDict(metadata_test))))
61-
61+
6262
md.update({'a': 'aval', 'b': 'bval'})
6363
assert_equal(md.data, dict(metadata_test))
6464

@@ -310,7 +310,7 @@ def test_matrix():
310310

311311
assert_raises(ci.Cifti2HeaderError, m.insert, 0, mim_none)
312312
assert_equal(m.mapped_indices, [])
313-
313+
314314
h = ci.Cifti2Header(matrix=m)
315315
assert_equal(m.mapped_indices, [])
316316
m.insert(0, mim_0)

nibabel/externals/netcdf.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
import numpy as np # noqa
3939
from ..py3k import asbytes, asstr
40-
from numpy import fromstring, ndarray, dtype, empty, array, asarray
40+
from numpy import frombuffer, ndarray, dtype, empty, array, asarray
4141
from numpy import little_endian as LITTLE_ENDIAN
4242
from functools import reduce
4343

@@ -519,7 +519,7 @@ def _read(self):
519519
if not magic == b'CDF':
520520
raise TypeError("Error: %s is not a valid NetCDF 3 file" %
521521
self.filename)
522-
self.__dict__['version_byte'] = fromstring(self.fp.read(1), '>b')[0]
522+
self.__dict__['version_byte'] = frombuffer(self.fp.read(1), '>b')[0]
523523

524524
# Read file headers and set data.
525525
self._read_numrecs()
@@ -608,7 +608,7 @@ def _read_var_array(self):
608608
# Calculate size to avoid problems with vsize (above)
609609
a_size = reduce(mul, shape, 1) * size
610610
if self.file_bytes >= 0 and begin_ + a_size > self.file_bytes:
611-
data = fromstring(b'\x00'*a_size, dtype=dtype_)
611+
data = frombuffer(b'\x00'*a_size, dtype=dtype_)
612612
elif self.use_mmap:
613613
mm = mmap(self.fp.fileno(), begin_+a_size, access=ACCESS_READ)
614614
data = ndarray.__new__(ndarray, shape, dtype=dtype_,
@@ -622,7 +622,7 @@ def _read_var_array(self):
622622
buf = self.fp.read(a_size)
623623
if len(buf) < a_size:
624624
buf = b'\x00'*a_size
625-
data = fromstring(buf, dtype=dtype_)
625+
data = frombuffer(buf, dtype=dtype_)
626626
data.shape = shape
627627
self.fp.seek(pos)
628628

@@ -644,7 +644,7 @@ def _read_var_array(self):
644644
else:
645645
pos = self.fp.tell()
646646
self.fp.seek(begin)
647-
rec_array = fromstring(self.fp.read(self._recs*self._recsize), dtype=dtypes)
647+
rec_array = frombuffer(self.fp.read(self._recs*self._recsize), dtype=dtypes)
648648
rec_array.shape = (self._recs,)
649649
self.fp.seek(pos)
650650

@@ -687,7 +687,7 @@ def _read_values(self):
687687
self.fp.read(-count % 4) # read padding
688688

689689
if typecode is not 'c':
690-
values = fromstring(values, dtype='>%s' % typecode)
690+
values = frombuffer(values, dtype='>%s' % typecode)
691691
if values.shape == (1,):
692692
values = values[0]
693693
else:
@@ -705,14 +705,14 @@ def _pack_int(self, value):
705705
_pack_int32 = _pack_int
706706

707707
def _unpack_int(self):
708-
return int(fromstring(self.fp.read(4), '>i')[0])
708+
return int(frombuffer(self.fp.read(4), '>i')[0])
709709
_unpack_int32 = _unpack_int
710710

711711
def _pack_int64(self, value):
712712
self.fp.write(array(value, '>q').tostring())
713713

714714
def _unpack_int64(self):
715-
return fromstring(self.fp.read(8), '>q')[0]
715+
return frombuffer(self.fp.read(8), '>q')[0]
716716

717717
def _pack_string(self, s):
718718
count = len(s)

nibabel/gifti/parse_gifti_fast.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data):
4747
dec = base64.b64decode(data.encode('ascii'))
4848
dt = data_type_codes.type[datatype]
4949
sh = tuple(shape)
50-
newarr = np.fromstring(dec, dtype=dt)
50+
newarr = np.frombuffer(dec, dtype=dt)
5151
if len(newarr.shape) != len(sh):
5252
newarr = newarr.reshape(sh, order=ord)
5353

@@ -59,7 +59,7 @@ def read_data_block(encoding, endian, ordering, datatype, shape, data):
5959
zdec = zlib.decompress(dec)
6060
dt = data_type_codes.type[datatype]
6161
sh = tuple(shape)
62-
newarr = np.fromstring(zdec, dtype=dt)
62+
newarr = np.frombuffer(zdec, dtype=dt)
6363
if len(newarr.shape) != len(sh):
6464
newarr = newarr.reshape(sh, order=ord)
6565

nibabel/info.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,5 @@ def cmp_pkg_version(version_str, pkg_version_str=__version__):
209209
ISRELEASE = _version_extra == ''
210210
VERSION = __version__
211211
PROVIDES = ["nibabel", 'nisext']
212-
REQUIRES = ["numpy (>=%s)" % NUMPY_MIN_VERSION]
212+
REQUIRES = ["numpy>=%s" % NUMPY_MIN_VERSION,
213+
'bz2file; python_version < "3.0"']

nibabel/nifti1.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ def from_fileobj(klass, fileobj, size, byteswap):
579579
# otherwise there should be a full extension header
580580
if not len(ext_def) == 8:
581581
raise HeaderDataError('failed to read extension header')
582-
ext_def = np.fromstring(ext_def, dtype=np.int32)
582+
ext_def = np.frombuffer(ext_def, dtype=np.int32)
583583
if byteswap:
584584
ext_def = ext_def.byteswap()
585585
# be extra verbose

nibabel/openers.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
""" Context manager openers for various fileobject types
1010
"""
1111

12-
import bz2
12+
import sys
13+
if sys.version_info[0] < 3:
14+
from bz2file import BZ2File
15+
else:
16+
from bz2 import BZ2File
1317
import gzip
1418
import sys
1519
import warnings
@@ -127,7 +131,7 @@ class Opener(object):
127131
for \*args
128132
"""
129133
gz_def = (_gzip_open, ('mode', 'compresslevel', 'keep_open'))
130-
bz2_def = (bz2.BZ2File, ('mode', 'buffering', 'compresslevel'))
134+
bz2_def = (BZ2File, ('mode', 'buffering', 'compresslevel'))
131135
compress_ext_map = {
132136
'.gz': gz_def,
133137
'.bz2': bz2_def,
@@ -209,6 +213,9 @@ def fileno(self):
209213
def read(self, *args, **kwargs):
210214
return self.fobj.read(*args, **kwargs)
211215

216+
def readinto(self, *args, **kwargs):
217+
return self.fobj.readinto(*args, **kwargs)
218+
212219
def write(self, *args, **kwargs):
213220
return self.fobj.write(*args, **kwargs)
214221

nibabel/streamlines/tck.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -405,18 +405,21 @@ def _read(cls, fileobj, header, buffer_size=4):
405405
n_streams = 0
406406

407407
while not eof:
408+
buff = bytearray(buffer_size)
409+
n_read = f.readinto(buff)
410+
eof = n_read != buffer_size
411+
if eof:
412+
buff = buff[:n_read]
408413

409-
bytes_read = f.read(buffer_size)
410-
buffs.append(bytes_read)
411-
eof = len(bytes_read) != buffer_size
414+
buffs.append(buff)
412415

413416
# Make sure we've read enough to find a streamline delimiter.
414-
if fiber_marker not in bytes_read:
417+
if fiber_marker not in buff:
415418
# If we've read the whole file, then fail.
416419
if eof:
417420
# Could have minimal buffering, and have read only the
418421
# EOF delimiter
419-
buffs = [b''.join(buffs)]
422+
buffs = [bytearray().join(buffs)]
420423
if not buffs[0] == eof_marker:
421424
raise DataError(
422425
"Cannot find a streamline delimiter. This file"
@@ -425,15 +428,13 @@ def _read(cls, fileobj, header, buffer_size=4):
425428
# Otherwise read a bit more.
426429
continue
427430

428-
all_parts = b''.join(buffs).split(fiber_marker)
431+
all_parts = bytearray().join(buffs).split(fiber_marker)
429432
point_parts, buffs = all_parts[:-1], all_parts[-1:]
430433
point_parts = [p for p in point_parts if p != b'']
431434

432435
for point_part in point_parts:
433436
# Read floats.
434437
pts = np.frombuffer(point_part, dtype=dtype)
435-
# Enforce ability to write to underlying bytes object
436-
pts.flags.writeable = True
437438
# Convert data to little-endian if needed.
438439
yield pts.astype('<f4', copy=False).reshape([-1, 3])
439440

nibabel/streamlines/tests/test_tck.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import numpy as np
44
from os.path import join as pjoin
55

6-
from six import BytesIO
6+
from io import BytesIO
77
from nibabel.py3k import asbytes
88

99
from ..array_sequence import ArraySequence

nibabel/streamlines/tests/test_trk.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def test_load_complex_file(self):
103103
def trk_with_bytes(self, trk_key='simple_trk_fname', endian='<'):
104104
""" Return example trk file bytes and struct view onto bytes """
105105
with open(DATA[trk_key], 'rb') as fobj:
106-
trk_bytes = fobj.read()
106+
trk_bytes = bytearray(fobj.read())
107107
dt = trk_module.header_2_dtype.newbyteorder(endian)
108108
trk_struct = np.ndarray((1,), dt, buffer=trk_bytes)
109109
trk_struct.flags.writeable = True

nibabel/streamlines/trk.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,11 @@ def _read_header(fileobj):
556556
start_position = fileobj.tell() if hasattr(fileobj, 'tell') else None
557557

558558
with Opener(fileobj) as f:
559-
560-
# Read the header in one block.
561-
header_str = f.read(header_2_dtype.itemsize)
562-
header_rec = np.fromstring(string=header_str, dtype=header_2_dtype)
563-
559+
# Reading directly from a file into a (mutable) bytearray enables a zero-copy
560+
# cast to a mutable numpy object with frombuffer
561+
header_buf = bytearray(header_2_dtype.itemsize)
562+
f.readinto(header_buf)
563+
header_rec = np.frombuffer(buffer=header_buf, dtype=header_2_dtype)
564564
# Check endianness
565565
endianness = native_code
566566
if header_rec['hdr_size'] != TrkFile.HEADER_SIZE:

nibabel/tests/test_openers.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
import os
1111
import contextlib
1212
from gzip import GzipFile
13-
from bz2 import BZ2File
1413
from io import BytesIO, UnsupportedOperation
1514
from distutils.version import StrictVersion
1615

1716
from ..py3k import asstr, asbytes
18-
from ..openers import Opener, ImageOpener, HAVE_INDEXED_GZIP
17+
from ..openers import Opener, ImageOpener, HAVE_INDEXED_GZIP, BZ2File
1918
from ..tmpdirs import InTemporaryDirectory
2019
from ..volumeutils import BinOpener
2120

nibabel/tests/test_volumeutils.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
_dt_min_max,
4747
_write_data,
4848
)
49-
from ..openers import Opener
49+
from ..openers import Opener, BZ2File
5050
from ..casting import (floor_log2, type_info, OK_FLOATS, shared_range)
5151

5252
from numpy.testing import (assert_array_almost_equal,
@@ -71,7 +71,7 @@ def test__is_compressed_fobj():
7171
with InTemporaryDirectory():
7272
for ext, opener, compressed in (('', open, False),
7373
('.gz', gzip.open, True),
74-
('.bz2', bz2.BZ2File, True)):
74+
('.bz2', BZ2File, True)):
7575
fname = 'test.bin' + ext
7676
for mode in ('wb', 'rb'):
7777
fobj = opener(fname, mode)
@@ -94,7 +94,7 @@ def make_array(n, bytes):
9494
with InTemporaryDirectory():
9595
for n, opener in itertools.product(
9696
(256, 1024, 2560, 25600),
97-
(open, gzip.open, bz2.BZ2File)):
97+
(open, gzip.open, BZ2File)):
9898
in_arr = np.arange(n, dtype=dtype)
9999
# Write array to file
100100
fobj_w = opener(fname, 'wb')
@@ -103,7 +103,8 @@ def make_array(n, bytes):
103103
# Read back from file
104104
fobj_r = opener(fname, 'rb')
105105
try:
106-
contents1 = fobj_r.read()
106+
contents1 = bytearray(4 * n)
107+
fobj_r.readinto(contents1)
107108
# Second element is 1
108109
assert_false(contents1[0:8] == b'\x00' * 8)
109110
out_arr = make_array(n, contents1)
@@ -114,7 +115,8 @@ def make_array(n, bytes):
114115
assert_equal(contents1[:8], b'\x00' * 8)
115116
# Reread, to get unmodified contents
116117
fobj_r.seek(0)
117-
contents2 = fobj_r.read()
118+
contents2 = bytearray(4 * n)
119+
fobj_r.readinto(contents2)
118120
out_arr2 = make_array(n, contents2)
119121
assert_array_equal(in_arr, out_arr2)
120122
assert_equal(out_arr[1], 0)

nibabel/volumeutils.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import sys
1313
import warnings
1414
import gzip
15-
import bz2
1615
from collections import OrderedDict
1716
from os.path import exists, splitext
1817
from operator import mul
@@ -21,7 +20,7 @@
2120
import numpy as np
2221

2322
from .casting import (shared_range, type_info, OK_FLOATS)
24-
from .openers import Opener
23+
from .openers import Opener, BZ2File
2524
from .deprecated import deprecate_with_version
2625
from .externals.oset import OrderedSet
2726

@@ -40,10 +39,7 @@
4039
default_compresslevel = 1
4140

4241
#: file-like classes known to hold compressed data
43-
COMPRESSED_FILE_LIKES = (gzip.GzipFile, bz2.BZ2File)
44-
45-
#: file-like classes known to return string values that are safe to modify
46-
SAFE_STRINGERS = (gzip.GzipFile, bz2.BZ2File)
42+
COMPRESSED_FILE_LIKES = (gzip.GzipFile, BZ2File)
4743

4844

4945
class Recoder(object):
@@ -530,7 +526,7 @@ def array_from_file(shape, in_dtype, infile, offset=0, order='F', mmap=True):
530526
else:
531527
data_bytes = infile.read(n_bytes)
532528
n_read = len(data_bytes)
533-
needs_copy = not isinstance(infile, SAFE_STRINGERS)
529+
needs_copy = True
534530
if n_bytes != n_read:
535531
raise IOError('Expected {0} bytes, got {1} bytes from {2}\n'
536532
' - could the file be damaged?'.format(

setup.py

+4-9
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,7 @@
1919
if os.path.exists('MANIFEST'):
2020
os.remove('MANIFEST')
2121

22-
# For some commands, use setuptools.
23-
if len(set(('develop', 'bdist_egg', 'bdist_rpm', 'bdist', 'bdist_dumb',
24-
'install_egg_info', 'egg_info', 'easy_install', 'bdist_wheel',
25-
'bdist_mpkg')).intersection(sys.argv)) > 0:
26-
# setup_egg imports setuptools setup, thus monkeypatching distutils.
27-
import setup_egg # noqa
28-
29-
from distutils.core import setup
22+
from setuptools import setup
3023

3124
# Commit hash writing, and dependency checking
3225
from nisext.sexts import (get_comrec_build, package_check, install_scripts_bat,
@@ -77,8 +70,8 @@ def main(**extra_args):
7770
author_email=INFO.AUTHOR_EMAIL,
7871
platforms=INFO.PLATFORMS,
7972
version=INFO.VERSION,
80-
requires=INFO.REQUIRES,
8173
provides=INFO.PROVIDES,
74+
install_requires=INFO.REQUIRES,
8275
packages = ['nibabel',
8376
'nibabel.externals',
8477
'nibabel.externals.tests',
@@ -127,4 +120,6 @@ def main(**extra_args):
127120

128121

129122
if __name__ == "__main__":
123+
# Do not use nisext's dynamically updated install_requires
124+
extra_setuptools_args.pop('install_requires', None)
130125
main(**extra_setuptools_args)

0 commit comments

Comments
 (0)