17
17
18
18
"""
19
19
20
-
21
20
# TODO:
22
21
# * properly implement ``_FillValue``.
23
22
# * fix character variables.
35
34
__all__ = ['netcdf_file' , 'netcdf_variable' ]
36
35
37
36
38
- import sys
39
37
import warnings
40
38
import weakref
41
39
from operator import mul
42
- from collections import OrderedDict
40
+ from platform import python_implementation
43
41
44
42
import mmap as mm
45
43
46
44
import numpy as np
47
- from numpy .compat import asbytes , asstr
48
45
from numpy import frombuffer , dtype , empty , array , asarray
49
46
from numpy import little_endian as LITTLE_ENDIAN
50
47
from functools import reduce
51
48
52
- IS_PYPY = ('__pypy__' in sys .modules )
49
+
50
+ IS_PYPY = python_implementation () == 'PyPy'
53
51
54
52
ABSENT = b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
55
53
ZERO = b'\x00 \x00 \x00 \x00 '
97
95
('S' , 1 ): NC_CHAR }
98
96
99
97
100
- class netcdf_file ( object ) :
98
+ class netcdf_file :
101
99
"""
102
100
A file object for NetCDF data.
103
101
@@ -141,7 +139,7 @@ class netcdf_file(object):
141
139
NetCDF files are a self-describing binary data format. The file contains
142
140
metadata that describes the dimensions and variables in the file. More
143
141
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
145
143
are three main sections to a NetCDF data structure:
146
144
147
145
1. Dimensions
@@ -180,67 +178,50 @@ class netcdf_file(object):
180
178
--------
181
179
To create a NetCDF file:
182
180
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()
199
188
200
189
Note the assignment of ``arange(10)`` to ``time[:]``. Exposing the slice
201
190
of the time variable allows for the data to be set in the object, rather
202
191
than letting ``arange(10)`` overwrite the ``time`` variable.
203
192
204
193
To read the NetCDF file we just created:
205
194
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
216
205
217
206
NetCDF files, when opened read-only, return arrays that refer
218
207
directly to memory-mapped data on disk:
219
208
220
- >>> data = time[:]
221
- >>> data.base.base # doctest: +ELLIPSIS
222
- <mmap.mmap ...>
209
+ >>> data = time[:]
223
210
224
211
If the data is to be processed after the file is closed, it needs
225
212
to be copied to main memory:
226
213
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
232
218
233
219
A NetCDF file can also be used as context manager:
234
220
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'
240
224
241
- >>> del f # needed for windows unlink
242
- >>> os.unlink(fname)
243
- >>> os.rmdir(tmp_pth)
244
225
"""
245
226
def __init__ (self , filename , mode = 'r' , mmap = None , version = 1 ,
246
227
maskandscale = False ):
@@ -274,8 +255,8 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
274
255
self .version_byte = version
275
256
self .maskandscale = maskandscale
276
257
277
- self .dimensions = OrderedDict ()
278
- self .variables = OrderedDict ()
258
+ self .dimensions = {}
259
+ self .variables = {}
279
260
280
261
self ._dims = []
281
262
self ._recs = 0
@@ -287,7 +268,7 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
287
268
self ._mm = mm .mmap (self .fp .fileno (), 0 , access = mm .ACCESS_READ )
288
269
self ._mm_buf = np .frombuffer (self ._mm , dtype = np .int8 )
289
270
290
- self ._attributes = OrderedDict ()
271
+ self ._attributes = {}
291
272
292
273
if mode in 'ra' :
293
274
self ._read ()
@@ -307,7 +288,7 @@ def close(self):
307
288
try :
308
289
self .flush ()
309
290
finally :
310
- self .variables = OrderedDict ()
291
+ self .variables = {}
311
292
if self ._mm_buf is not None :
312
293
ref = weakref .ref (self ._mm_buf )
313
294
self ._mm_buf = None
@@ -339,7 +320,7 @@ def createDimension(self, name, length):
339
320
Adds a dimension to the Dimension section of the NetCDF data structure.
340
321
341
322
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
343
324
a variable using `createVariable`, referring to this dimension.
344
325
345
326
Parameters
@@ -392,7 +373,7 @@ def createVariable(self, name, type, dimensions):
392
373
393
374
"""
394
375
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
396
377
397
378
type = dtype (type )
398
379
typecode , size = type .char , type .itemsize
@@ -499,7 +480,7 @@ def _write_var_metadata(self, name):
499
480
self ._write_att_array (var ._attributes )
500
481
501
482
nc_type = REVERSE [var .typecode (), var .itemsize ()]
502
- self .fp .write (asbytes ( nc_type ) )
483
+ self .fp .write (nc_type )
503
484
504
485
if not var .isrec :
505
486
vsize = var .data .size * var .data .itemsize
@@ -570,12 +551,9 @@ def _write_att_values(self, values):
570
551
if hasattr (values , 'dtype' ):
571
552
nc_type = REVERSE [values .dtype .char , values .dtype .itemsize ]
572
553
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
579
557
if isinstance (values , (str , bytes )):
580
558
sample = values
581
559
else :
@@ -590,12 +568,12 @@ def _write_att_values(self, values):
590
568
591
569
typecode , size = TYPEMAP [nc_type ]
592
570
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'
594
572
dtype_ = 'S' if dtype_ == '>c' else dtype_
595
573
596
574
values = asarray (values , dtype = dtype_ )
597
575
598
- self .fp .write (asbytes ( nc_type ) )
576
+ self .fp .write (nc_type )
599
577
600
578
if values .dtype .char == 'S' :
601
579
nelems = values .itemsize
@@ -634,7 +612,7 @@ def _read_dim_array(self):
634
612
count = self ._unpack_int ()
635
613
636
614
for dim in range (count ):
637
- name = asstr ( self ._unpack_string ())
615
+ name = self ._unpack_string (). decode ( 'latin1' )
638
616
length = self ._unpack_int () or None # None for record dimension
639
617
self .dimensions [name ] = length
640
618
self ._dims .append (name ) # preserve order
@@ -649,9 +627,9 @@ def _read_att_array(self):
649
627
raise ValueError ("Unexpected header." )
650
628
count = self ._unpack_int ()
651
629
652
- attributes = OrderedDict ()
630
+ attributes = {}
653
631
for attr in range (count ):
654
- name = asstr ( self ._unpack_string ())
632
+ name = self ._unpack_string (). decode ( 'latin1' )
655
633
attributes [name ] = self ._read_att_values ()
656
634
return attributes
657
635
@@ -667,7 +645,7 @@ def _read_var_array(self):
667
645
for var in range (count ):
668
646
(name , dimensions , shape , attributes ,
669
647
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
671
649
# Note that vsize is the product of the dimension lengths
672
650
# (omitting the record dimension) and the number of bytes
673
651
# per value (determined from the type), increased to the
@@ -742,7 +720,7 @@ def _read_var_array(self):
742
720
self .variables [var ].__dict__ ['data' ] = rec_array [var ]
743
721
744
722
def _read_var (self ):
745
- name = asstr ( self ._unpack_string ())
723
+ name = self ._unpack_string (). decode ( 'latin1' )
746
724
dimensions = []
747
725
shape = []
748
726
dims = self ._unpack_int ()
@@ -807,7 +785,7 @@ def _unpack_int64(self):
807
785
def _pack_string (self , s ):
808
786
count = len (s )
809
787
self ._pack_int (count )
810
- self .fp .write (asbytes ( s ))
788
+ self .fp .write (s . encode ( 'latin1' ))
811
789
self .fp .write (b'\x00 ' * (- count % 4 )) # pad
812
790
813
791
def _unpack_string (self ):
@@ -817,7 +795,7 @@ def _unpack_string(self):
817
795
return s
818
796
819
797
820
- class netcdf_variable ( object ) :
798
+ class netcdf_variable :
821
799
"""
822
800
A data object for netcdf files.
823
801
@@ -845,13 +823,13 @@ class netcdf_variable(object):
845
823
size : int
846
824
Desired element size for the data array.
847
825
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
849
827
variable's dimensions.
850
828
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
852
830
same order of the dimension lengths given by `shape`.
853
831
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
855
833
become attributes for the netcdf_variable object.
856
834
maskandscale : bool, optional
857
835
Whether to automatically scale and/or mask data based on attributes.
@@ -880,7 +858,7 @@ def __init__(self, data, typecode, size, shape, dimensions,
880
858
self .dimensions = dimensions
881
859
self .maskandscale = maskandscale
882
860
883
- self ._attributes = attributes or OrderedDict ()
861
+ self ._attributes = attributes or {}
884
862
for k , v in self ._attributes .items ():
885
863
self .__dict__ [k ] = v
886
864
@@ -949,7 +927,7 @@ def assignValue(self, value):
949
927
# memory-mapped array causes a seg. fault.
950
928
# See NumPy ticket #1622, and SciPy ticket #1202.
951
929
# 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.
953
931
raise RuntimeError ("variable is not writeable" )
954
932
955
933
self .data .itemset (value )
@@ -961,7 +939,7 @@ def typecode(self):
961
939
Returns
962
940
-------
963
941
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).
965
943
966
944
"""
967
945
return self ._typecode
@@ -973,7 +951,7 @@ def itemsize(self):
973
951
Returns
974
952
-------
975
953
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).
977
955
978
956
"""
979
957
return self ._size
0 commit comments