1717
1818"""
1919
20-
2120# TODO:
2221# * properly implement ``_FillValue``.
2322# * fix character variables.
3534__all__ = ['netcdf_file' , 'netcdf_variable' ]
3635
3736
38- import sys
3937import warnings
4038import weakref
4139from operator import mul
42- from collections import OrderedDict
40+ from platform import python_implementation
4341
4442import mmap as mm
4543
4644import numpy as np
47- from numpy .compat import asbytes , asstr
4845from numpy import frombuffer , dtype , empty , array , asarray
4946from numpy import little_endian as LITTLE_ENDIAN
5047from functools import reduce
5148
52- IS_PYPY = ('__pypy__' in sys .modules )
49+
50+ IS_PYPY = python_implementation () == 'PyPy'
5351
5452ABSENT = b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
5553ZERO = b'\x00 \x00 \x00 \x00 '
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