Skip to content

Commit a7f5d45

Browse files
committed
MNT: Update netcdf to latest scipy
1 parent 9fbddef commit a7f5d45

File tree

1 file changed

+59
-78
lines changed

1 file changed

+59
-78
lines changed

nibabel/externals/netcdf.py

+59-78
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
1818
"""
1919

20-
2120
# TODO:
2221
# * properly implement ``_FillValue``.
2322
# * fix character variables.
@@ -35,21 +34,20 @@
3534
__all__ = ['netcdf_file', 'netcdf_variable']
3635

3736

38-
import sys
3937
import warnings
4038
import weakref
4139
from operator import mul
42-
from collections import OrderedDict
40+
from platform import python_implementation
4341

4442
import mmap as mm
4543

4644
import numpy as np
47-
from numpy.compat import asbytes, asstr
4845
from numpy import frombuffer, dtype, empty, array, asarray
4946
from numpy import little_endian as LITTLE_ENDIAN
5047
from functools import reduce
5148

52-
IS_PYPY = ('__pypy__' in sys.modules)
49+
50+
IS_PYPY = python_implementation() == 'PyPy'
5351

5452
ABSENT = b'\x00\x00\x00\x00\x00\x00\x00\x00'
5553
ZERO = b'\x00\x00\x00\x00'
@@ -97,7 +95,7 @@
9795
('S', 1): NC_CHAR}
9896

9997

100-
class netcdf_file(object):
98+
class netcdf_file:
10199
"""
102100
A file object for NetCDF data.
103101
@@ -141,7 +139,7 @@ class netcdf_file(object):
141139
NetCDF files are a self-describing binary data format. The file contains
142140
metadata that describes the dimensions and variables in the file. More
143141
details about NetCDF files can be found `here
144-
<https://www.unidata.ucar.edu/software/netcdf/docs/user_guide.html>`__. There
142+
<https://www.unidata.ucar.edu/software/netcdf/guide_toc.html>`__. There
145143
are three main sections to a NetCDF data structure:
146144
147145
1. Dimensions
@@ -180,67 +178,53 @@ class netcdf_file(object):
180178
--------
181179
To create a NetCDF file:
182180
183-
Make a temporary file for testing:
184-
185-
>>> import os
186-
>>> from tempfile import mkdtemp
187-
>>> tmp_pth = mkdtemp()
188-
>>> fname = os.path.join(tmp_pth, 'test.nc')
189-
190-
Then:
191-
192-
>>> f = netcdf_file(fname, 'w')
193-
>>> f.history = 'Created for a test'
194-
>>> f.createDimension('time', 10)
195-
>>> time = f.createVariable('time', 'i', ('time',))
196-
>>> time[:] = np.arange(10)
197-
>>> time.units = 'days since 2008-01-01'
198-
>>> f.close()
181+
>>> from scipy.io import netcdf_file
182+
>>> f = netcdf_file('simple.nc', 'w')
183+
>>> f.history = 'Created for a test'
184+
>>> f.createDimension('time', 10)
185+
>>> time = f.createVariable('time', 'i', ('time',))
186+
>>> time[:] = np.arange(10)
187+
>>> time.units = 'days since 2008-01-01'
188+
>>> f.close()
199189
200190
Note the assignment of ``arange(10)`` to ``time[:]``. Exposing the slice
201191
of the time variable allows for the data to be set in the object, rather
202192
than letting ``arange(10)`` overwrite the ``time`` variable.
203193
204194
To read the NetCDF file we just created:
205195
206-
>>> f = netcdf_file(fname, 'r')
207-
>>> f.history == b'Created for a test'
208-
True
209-
>>> time = f.variables['time']
210-
>>> time.units == b'days since 2008-01-01'
211-
True
212-
>>> time.shape == (10,)
213-
True
214-
>>> time[-1]
215-
9
196+
>>> from scipy.io import netcdf_file
197+
>>> f = netcdf_file('simple.nc', 'r')
198+
>>> print(f.history)
199+
b'Created for a test'
200+
>>> time = f.variables['time']
201+
>>> print(time.units)
202+
b'days since 2008-01-01'
203+
>>> print(time.shape)
204+
(10,)
205+
>>> print(time[-1])
206+
9
216207
217208
NetCDF files, when opened read-only, return arrays that refer
218209
directly to memory-mapped data on disk:
219210
220-
>>> data = time[:]
221-
>>> data.base.base # doctest: +ELLIPSIS
222-
<mmap.mmap ...>
211+
>>> data = time[:]
223212
224213
If the data is to be processed after the file is closed, it needs
225214
to be copied to main memory:
226215
227-
>>> data = time[:].copy()
228-
>>> del time # References to mmap'd objects can delay full closure
229-
>>> f.close()
230-
>>> data.mean()
231-
4.5
216+
>>> data = time[:].copy()
217+
>>> f.close()
218+
>>> data.mean()
219+
4.5
232220
233221
A NetCDF file can also be used as context manager:
234222
235-
>>> with netcdf_file(fname, 'r') as f:
236-
... print(f.variables['time'].shape == (10,))
237-
True
238-
239-
Delete our temporary directory and file:
223+
>>> from scipy.io import netcdf_file
224+
>>> with netcdf_file('simple.nc', 'r') as f:
225+
... print(f.history)
226+
b'Created for a test'
240227
241-
>>> del f # needed for windows unlink
242-
>>> os.unlink(fname)
243-
>>> os.rmdir(tmp_pth)
244228
"""
245229
def __init__(self, filename, mode='r', mmap=None, version=1,
246230
maskandscale=False):
@@ -274,8 +258,8 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
274258
self.version_byte = version
275259
self.maskandscale = maskandscale
276260

277-
self.dimensions = OrderedDict()
278-
self.variables = OrderedDict()
261+
self.dimensions = {}
262+
self.variables = {}
279263

280264
self._dims = []
281265
self._recs = 0
@@ -287,7 +271,7 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
287271
self._mm = mm.mmap(self.fp.fileno(), 0, access=mm.ACCESS_READ)
288272
self._mm_buf = np.frombuffer(self._mm, dtype=np.int8)
289273

290-
self._attributes = OrderedDict()
274+
self._attributes = {}
291275

292276
if mode in 'ra':
293277
self._read()
@@ -307,7 +291,7 @@ def close(self):
307291
try:
308292
self.flush()
309293
finally:
310-
self.variables = OrderedDict()
294+
self.variables = {}
311295
if self._mm_buf is not None:
312296
ref = weakref.ref(self._mm_buf)
313297
self._mm_buf = None
@@ -339,7 +323,7 @@ def createDimension(self, name, length):
339323
Adds a dimension to the Dimension section of the NetCDF data structure.
340324
341325
Note that this function merely adds a new dimension that the variables can
342-
reference. The values for the dimension, if desired, should be added as
326+
reference. The values for the dimension, if desired, should be added as
343327
a variable using `createVariable`, referring to this dimension.
344328
345329
Parameters
@@ -392,7 +376,7 @@ def createVariable(self, name, type, dimensions):
392376
393377
"""
394378
shape = tuple([self.dimensions[dim] for dim in dimensions])
395-
shape_ = tuple([dim or 0 for dim in shape]) # replace None with 0 for numpy
379+
shape_ = tuple([dim or 0 for dim in shape]) # replace None with 0 for NumPy
396380

397381
type = dtype(type)
398382
typecode, size = type.char, type.itemsize
@@ -499,7 +483,7 @@ def _write_var_metadata(self, name):
499483
self._write_att_array(var._attributes)
500484

501485
nc_type = REVERSE[var.typecode(), var.itemsize()]
502-
self.fp.write(asbytes(nc_type))
486+
self.fp.write(nc_type)
503487

504488
if not var.isrec:
505489
vsize = var.data.size * var.data.itemsize
@@ -570,12 +554,9 @@ def _write_att_values(self, values):
570554
if hasattr(values, 'dtype'):
571555
nc_type = REVERSE[values.dtype.char, values.dtype.itemsize]
572556
else:
573-
types = [
574-
(int, NC_INT),
575-
(float, NC_FLOAT),
576-
(str, NC_CHAR)
577-
]
578-
# bytes index into scalars in py3k. Check for "string" types
557+
types = [(int, NC_INT), (float, NC_FLOAT), (str, NC_CHAR)]
558+
559+
# bytes index into scalars in py3k. Check for "string" types
579560
if isinstance(values, (str, bytes)):
580561
sample = values
581562
else:
@@ -590,12 +571,12 @@ def _write_att_values(self, values):
590571

591572
typecode, size = TYPEMAP[nc_type]
592573
dtype_ = '>%s' % typecode
593-
# asarray() dies with bytes and '>c' in py3k. Change to 'S'
574+
# asarray() dies with bytes and '>c' in py3k. Change to 'S'
594575
dtype_ = 'S' if dtype_ == '>c' else dtype_
595576

596577
values = asarray(values, dtype=dtype_)
597578

598-
self.fp.write(asbytes(nc_type))
579+
self.fp.write(nc_type)
599580

600581
if values.dtype.char == 'S':
601582
nelems = values.itemsize
@@ -634,7 +615,7 @@ def _read_dim_array(self):
634615
count = self._unpack_int()
635616

636617
for dim in range(count):
637-
name = asstr(self._unpack_string())
618+
name = self._unpack_string().decode('latin1')
638619
length = self._unpack_int() or None # None for record dimension
639620
self.dimensions[name] = length
640621
self._dims.append(name) # preserve order
@@ -649,9 +630,9 @@ def _read_att_array(self):
649630
raise ValueError("Unexpected header.")
650631
count = self._unpack_int()
651632

652-
attributes = OrderedDict()
633+
attributes = {}
653634
for attr in range(count):
654-
name = asstr(self._unpack_string())
635+
name = self._unpack_string().decode('latin1')
655636
attributes[name] = self._read_att_values()
656637
return attributes
657638

@@ -667,7 +648,7 @@ def _read_var_array(self):
667648
for var in range(count):
668649
(name, dimensions, shape, attributes,
669650
typecode, size, dtype_, begin_, vsize) = self._read_var()
670-
# https://www.unidata.ucar.edu/software/netcdf/docs/user_guide.html
651+
# https://www.unidata.ucar.edu/software/netcdf/guide_toc.html
671652
# Note that vsize is the product of the dimension lengths
672653
# (omitting the record dimension) and the number of bytes
673654
# per value (determined from the type), increased to the
@@ -742,7 +723,7 @@ def _read_var_array(self):
742723
self.variables[var].__dict__['data'] = rec_array[var]
743724

744725
def _read_var(self):
745-
name = asstr(self._unpack_string())
726+
name = self._unpack_string().decode('latin1')
746727
dimensions = []
747728
shape = []
748729
dims = self._unpack_int()
@@ -807,7 +788,7 @@ def _unpack_int64(self):
807788
def _pack_string(self, s):
808789
count = len(s)
809790
self._pack_int(count)
810-
self.fp.write(asbytes(s))
791+
self.fp.write(s.encode('latin1'))
811792
self.fp.write(b'\x00' * (-count % 4)) # pad
812793

813794
def _unpack_string(self):
@@ -817,7 +798,7 @@ def _unpack_string(self):
817798
return s
818799

819800

820-
class netcdf_variable(object):
801+
class netcdf_variable:
821802
"""
822803
A data object for netcdf files.
823804
@@ -845,13 +826,13 @@ class netcdf_variable(object):
845826
size : int
846827
Desired element size for the data array.
847828
shape : sequence of ints
848-
The shape of the array. This should match the lengths of the
829+
The shape of the array. This should match the lengths of the
849830
variable's dimensions.
850831
dimensions : sequence of strings
851-
The names of the dimensions used by the variable. Must be in the
832+
The names of the dimensions used by the variable. Must be in the
852833
same order of the dimension lengths given by `shape`.
853834
attributes : dict, optional
854-
Attribute values (any type) keyed by string names. These attributes
835+
Attribute values (any type) keyed by string names. These attributes
855836
become attributes for the netcdf_variable object.
856837
maskandscale : bool, optional
857838
Whether to automatically scale and/or mask data based on attributes.
@@ -880,7 +861,7 @@ def __init__(self, data, typecode, size, shape, dimensions,
880861
self.dimensions = dimensions
881862
self.maskandscale = maskandscale
882863

883-
self._attributes = attributes or OrderedDict()
864+
self._attributes = attributes or {}
884865
for k, v in self._attributes.items():
885866
self.__dict__[k] = v
886867

@@ -949,7 +930,7 @@ def assignValue(self, value):
949930
# memory-mapped array causes a seg. fault.
950931
# See NumPy ticket #1622, and SciPy ticket #1202.
951932
# This check for `writeable` can be removed when the oldest version
952-
# of numpy still supported by scipy contains the fix for #1622.
933+
# of NumPy still supported by scipy contains the fix for #1622.
953934
raise RuntimeError("variable is not writeable")
954935

955936
self.data.itemset(value)
@@ -961,7 +942,7 @@ def typecode(self):
961942
Returns
962943
-------
963944
typecode : char
964-
The character typecode of the variable (eg, 'i' for int).
945+
The character typecode of the variable (e.g., 'i' for int).
965946
966947
"""
967948
return self._typecode
@@ -973,7 +954,7 @@ def itemsize(self):
973954
Returns
974955
-------
975956
itemsize : int
976-
The element size of the variable (eg, 8 for float64).
957+
The element size of the variable (e.g., 8 for float64).
977958
978959
"""
979960
return self._size

0 commit comments

Comments
 (0)