diff --git a/Changelog b/Changelog index eca828891..2d6dda7b5 100644 --- a/Changelog +++ b/Changelog @@ -25,6 +25,9 @@ array dtypes with 'SN' string subtypes can now be used to define netcdf compound types (they get converted to ('S1',N) character array types automatically). + * always return masked array by default, even if there are no + masked values (too surprising to get ndarray or MaskedArray depending + on slice, issue #785). version 1.3.1 (tag v1.3.1rel) diff --git a/netCDF4/_netCDF4.pyx b/netCDF4/_netCDF4.pyx index b65938d7e..0fad1c1d7 100644 --- a/netCDF4/_netCDF4.pyx +++ b/netCDF4/_netCDF4.pyx @@ -4269,16 +4269,24 @@ rename a `netCDF4.Variable` attribute named `oldname` to `newname`.""" totalmask += data > validmax if fill_value is None and fval is not None: fill_value = fval + # if all else fails, use default _FillValue as fill_value + # for masked array. + if fill_value is None: + fill_value = default_fillvals[self.dtype.str[1:]] # create masked array with computed mask - if totalmask.any() and fill_value is not None: + if totalmask.any(): data = ma.masked_array(data,mask=totalmask,fill_value=fill_value) - # issue 515 scalar array with mask=True should be converted - # to numpy.ma.MaskedConstant to be consistent with slicing - # behavior of masked arrays. - if data.shape == () and data.mask.all(): - # return a scalar numpy masked constant not a 0-d masked array, - # so that data == numpy.ma.masked. - data = data[()] # changed from [...] (issue #662) + else: + # issue #785: always return masked array, if no values masked + # set mask=False. + data = ma.masked_array(data,mask=False,fill_value=fill_value) + # issue 515 scalar array with mask=True should be converted + # to numpy.ma.MaskedConstant to be consistent with slicing + # behavior of masked arrays. + if data.shape == () and data.mask.all(): + # return a scalar numpy masked constant not a 0-d masked array, + # so that data == numpy.ma.masked. + data = data[()] # changed from [...] (issue #662) return data def _assign_vlen(self, elem, data): diff --git a/test/tst_fancyslicing.py b/test/tst_fancyslicing.py index 742169562..fe86e5625 100644 --- a/test/tst_fancyslicing.py +++ b/test/tst_fancyslicing.py @@ -112,7 +112,9 @@ def test_get(self): # Empty boolean -- all False d1 = f.variables['data1'] m = np.zeros(xdim, bool) - assert_equal(d1[m], ()) + if np.__version__ > '1.9.0': + # fails for old numpy versions + assert_equal(d1[m], ()) # Check that no assignment is made d1[m] = 0 diff --git a/test/tst_scaled.py b/test/tst_scaled.py index 979310b06..b38244036 100755 --- a/test/tst_scaled.py +++ b/test/tst_scaled.py @@ -63,7 +63,8 @@ def test_unmasked(self): self.assertEqual(v.dtype, "i2") self.assertTrue(isinstance(v, np.ndarray)) - self.assertTrue(not isinstance(v, ma.core.MaskedArray)) + # issue 785: always return masked array by default + self.assertTrue(isinstance(v, ma.core.MaskedArray)) assert_array_almost_equal(v, self.v) f.close() @@ -107,7 +108,8 @@ def test_unmasked(self): self.assertEqual(v_scaled.dtype, "f8") self.assertTrue(isinstance(v_scaled, np.ndarray)) - self.assertTrue(not isinstance(v_scaled, ma.core.MaskedArray)) + # issue 785: always return masked array by default + self.assertTrue(isinstance(v_scaled, ma.core.MaskedArray)) assert_array_almost_equal(v_scaled, self.v_scaled) f.close() diff --git a/test/tst_slicing.py b/test/tst_slicing.py index b6a0215ba..7fa7cebb9 100644 --- a/test/tst_slicing.py +++ b/test/tst_slicing.py @@ -94,6 +94,10 @@ def test_0d(self): v[...] = 10 assert_array_equal(v[...], 10) assert_equal(v.shape, v[...].shape) + # issue #785: always return masked array + #assert(type(v[...]) == np.ndarray) + assert(type(v[...]) == np.ma.core.MaskedArray) + f.set_auto_mask(False) assert(type(v[...]) == np.ndarray) f.close()