Skip to content

Commit 9200261

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

File tree

1 file changed

+56
-78
lines changed

1 file changed

+56
-78
lines changed

nibabel/externals/netcdf.py

+56-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,50 @@ 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+
>>> f = netcdf_file('simple.nc', 'w')
182+
>>> f.history = 'Created for a test'
183+
>>> f.createDimension('time', 10)
184+
>>> time = f.createVariable('time', 'i', ('time',))
185+
>>> time[:] = np.arange(10)
186+
>>> time.units = 'days since 2008-01-01'
187+
>>> f.close()
199188
200189
Note the assignment of ``arange(10)`` to ``time[:]``. Exposing the slice
201190
of the time variable allows for the data to be set in the object, rather
202191
than letting ``arange(10)`` overwrite the ``time`` variable.
203192
204193
To read the NetCDF file we just created:
205194
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
195+
>>> f = netcdf_file('simple.nc', 'r')
196+
>>> print(f.history)
197+
b'Created for a test'
198+
>>> time = f.variables['time']
199+
>>> print(time.units)
200+
b'days since 2008-01-01'
201+
>>> print(time.shape)
202+
(10,)
203+
>>> print(time[-1])
204+
9
216205
217206
NetCDF files, when opened read-only, return arrays that refer
218207
directly to memory-mapped data on disk:
219208
220-
>>> data = time[:]
221-
>>> data.base.base # doctest: +ELLIPSIS
222-
<mmap.mmap ...>
209+
>>> data = time[:]
223210
224211
If the data is to be processed after the file is closed, it needs
225212
to be copied to main memory:
226213
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
214+
>>> data = time[:].copy()
215+
>>> f.close()
216+
>>> data.mean()
217+
4.5
232218
233219
A NetCDF file can also be used as context manager:
234220
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:
221+
>>> with netcdf_file('simple.nc', 'r') as f:
222+
... print(f.history)
223+
b'Created for a test'
240224
241-
>>> del f # needed for windows unlink
242-
>>> os.unlink(fname)
243-
>>> os.rmdir(tmp_pth)
244225
"""
245226
def __init__(self, filename, mode='r', mmap=None, version=1,
246227
maskandscale=False):
@@ -274,8 +255,8 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
274255
self.version_byte = version
275256
self.maskandscale = maskandscale
276257

277-
self.dimensions = OrderedDict()
278-
self.variables = OrderedDict()
258+
self.dimensions = {}
259+
self.variables = {}
279260

280261
self._dims = []
281262
self._recs = 0
@@ -287,7 +268,7 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
287268
self._mm = mm.mmap(self.fp.fileno(), 0, access=mm.ACCESS_READ)
288269
self._mm_buf = np.frombuffer(self._mm, dtype=np.int8)
289270

290-
self._attributes = OrderedDict()
271+
self._attributes = {}
291272

292273
if mode in 'ra':
293274
self._read()
@@ -307,7 +288,7 @@ def close(self):
307288
try:
308289
self.flush()
309290
finally:
310-
self.variables = OrderedDict()
291+
self.variables = {}
311292
if self._mm_buf is not None:
312293
ref = weakref.ref(self._mm_buf)
313294
self._mm_buf = None
@@ -339,7 +320,7 @@ def createDimension(self, name, length):
339320
Adds a dimension to the Dimension section of the NetCDF data structure.
340321
341322
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
323+
reference. The values for the dimension, if desired, should be added as
343324
a variable using `createVariable`, referring to this dimension.
344325
345326
Parameters
@@ -392,7 +373,7 @@ def createVariable(self, name, type, dimensions):
392373
393374
"""
394375
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
376+
shape_ = tuple([dim or 0 for dim in shape]) # replace None with 0 for NumPy
396377

397378
type = dtype(type)
398379
typecode, size = type.char, type.itemsize
@@ -499,7 +480,7 @@ def _write_var_metadata(self, name):
499480
self._write_att_array(var._attributes)
500481

501482
nc_type = REVERSE[var.typecode(), var.itemsize()]
502-
self.fp.write(asbytes(nc_type))
483+
self.fp.write(nc_type)
503484

504485
if not var.isrec:
505486
vsize = var.data.size * var.data.itemsize
@@ -570,12 +551,9 @@ def _write_att_values(self, values):
570551
if hasattr(values, 'dtype'):
571552
nc_type = REVERSE[values.dtype.char, values.dtype.itemsize]
572553
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
554+
types = [(int, NC_INT), (float, NC_FLOAT), (str, NC_CHAR)]
555+
556+
# bytes index into scalars in py3k. Check for "string" types
579557
if isinstance(values, (str, bytes)):
580558
sample = values
581559
else:
@@ -590,12 +568,12 @@ def _write_att_values(self, values):
590568

591569
typecode, size = TYPEMAP[nc_type]
592570
dtype_ = '>%s' % typecode
593-
# asarray() dies with bytes and '>c' in py3k. Change to 'S'
571+
# asarray() dies with bytes and '>c' in py3k. Change to 'S'
594572
dtype_ = 'S' if dtype_ == '>c' else dtype_
595573

596574
values = asarray(values, dtype=dtype_)
597575

598-
self.fp.write(asbytes(nc_type))
576+
self.fp.write(nc_type)
599577

600578
if values.dtype.char == 'S':
601579
nelems = values.itemsize
@@ -634,7 +612,7 @@ def _read_dim_array(self):
634612
count = self._unpack_int()
635613

636614
for dim in range(count):
637-
name = asstr(self._unpack_string())
615+
name = self._unpack_string().decode('latin1')
638616
length = self._unpack_int() or None # None for record dimension
639617
self.dimensions[name] = length
640618
self._dims.append(name) # preserve order
@@ -649,9 +627,9 @@ def _read_att_array(self):
649627
raise ValueError("Unexpected header.")
650628
count = self._unpack_int()
651629

652-
attributes = OrderedDict()
630+
attributes = {}
653631
for attr in range(count):
654-
name = asstr(self._unpack_string())
632+
name = self._unpack_string().decode('latin1')
655633
attributes[name] = self._read_att_values()
656634
return attributes
657635

@@ -667,7 +645,7 @@ def _read_var_array(self):
667645
for var in range(count):
668646
(name, dimensions, shape, attributes,
669647
typecode, size, dtype_, begin_, vsize) = self._read_var()
670-
# https://www.unidata.ucar.edu/software/netcdf/docs/user_guide.html
648+
# https://www.unidata.ucar.edu/software/netcdf/guide_toc.html
671649
# Note that vsize is the product of the dimension lengths
672650
# (omitting the record dimension) and the number of bytes
673651
# per value (determined from the type), increased to the
@@ -742,7 +720,7 @@ def _read_var_array(self):
742720
self.variables[var].__dict__['data'] = rec_array[var]
743721

744722
def _read_var(self):
745-
name = asstr(self._unpack_string())
723+
name = self._unpack_string().decode('latin1')
746724
dimensions = []
747725
shape = []
748726
dims = self._unpack_int()
@@ -807,7 +785,7 @@ def _unpack_int64(self):
807785
def _pack_string(self, s):
808786
count = len(s)
809787
self._pack_int(count)
810-
self.fp.write(asbytes(s))
788+
self.fp.write(s.encode('latin1'))
811789
self.fp.write(b'\x00' * (-count % 4)) # pad
812790

813791
def _unpack_string(self):
@@ -817,7 +795,7 @@ def _unpack_string(self):
817795
return s
818796

819797

820-
class netcdf_variable(object):
798+
class netcdf_variable:
821799
"""
822800
A data object for netcdf files.
823801
@@ -845,13 +823,13 @@ class netcdf_variable(object):
845823
size : int
846824
Desired element size for the data array.
847825
shape : sequence of ints
848-
The shape of the array. This should match the lengths of the
826+
The shape of the array. This should match the lengths of the
849827
variable's dimensions.
850828
dimensions : sequence of strings
851-
The names of the dimensions used by the variable. Must be in the
829+
The names of the dimensions used by the variable. Must be in the
852830
same order of the dimension lengths given by `shape`.
853831
attributes : dict, optional
854-
Attribute values (any type) keyed by string names. These attributes
832+
Attribute values (any type) keyed by string names. These attributes
855833
become attributes for the netcdf_variable object.
856834
maskandscale : bool, optional
857835
Whether to automatically scale and/or mask data based on attributes.
@@ -880,7 +858,7 @@ def __init__(self, data, typecode, size, shape, dimensions,
880858
self.dimensions = dimensions
881859
self.maskandscale = maskandscale
882860

883-
self._attributes = attributes or OrderedDict()
861+
self._attributes = attributes or {}
884862
for k, v in self._attributes.items():
885863
self.__dict__[k] = v
886864

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

955933
self.data.itemset(value)
@@ -961,7 +939,7 @@ def typecode(self):
961939
Returns
962940
-------
963941
typecode : char
964-
The character typecode of the variable (eg, 'i' for int).
942+
The character typecode of the variable (e.g., 'i' for int).
965943
966944
"""
967945
return self._typecode
@@ -973,7 +951,7 @@ def itemsize(self):
973951
Returns
974952
-------
975953
itemsize : int
976-
The element size of the variable (eg, 8 for float64).
954+
The element size of the variable (e.g., 8 for float64).
977955
978956
"""
979957
return self._size

0 commit comments

Comments
 (0)