summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatti Picus <matti.picus@gmail.com>2019-05-12 09:27:12 -0700
committerGitHub <noreply@github.com>2019-05-12 09:27:12 -0700
commitd2d589703d5ee743251fbc6ea045f28e1c5e9e71 (patch)
treedf60e9574f8a2af7ad29cfbde2ad68b8a2ed405a
parent798dce7d2f4d661eef2604bbd68fbe068c8d1ad6 (diff)
parent599dbade766ad3fecc82afa308803a2e0082c149 (diff)
downloadnumpy-d2d589703d5ee743251fbc6ea045f28e1c5e9e71.tar.gz
Merge pull request #10308 from eric-wieser/mask-attr-is-view
API: Make MaskedArray.mask return a view, rather than the underlying mask
-rw-r--r--doc/release/1.17.0-notes.rst10
-rw-r--r--numpy/lib/tests/test_function_base.py2
-rw-r--r--numpy/ma/core.py4
-rw-r--r--numpy/ma/tests/test_core.py11
-rw-r--r--numpy/ma/tests/test_old_ma.py14
5 files changed, 30 insertions, 11 deletions
diff --git a/doc/release/1.17.0-notes.rst b/doc/release/1.17.0-notes.rst
index dcadc009b..4f9134e0b 100644
--- a/doc/release/1.17.0-notes.rst
+++ b/doc/release/1.17.0-notes.rst
@@ -66,6 +66,16 @@ zero::
>>> np.zeros(10)//1
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
+``MaskedArray.mask`` now returns a view of the mask, not the mask itself
+------------------------------------------------------------------------
+Returning the mask itself was unsafe, as it could be reshaped in place which
+would violate expectations of the masked array code. It's behavior is now
+consistent with the ``.data`` attribute, which also returns a view.
+
+The underlying mask can still be accessed with ``._mask`` if it is needed.
+Tests that contain ``assert x.mask is not y.mask`` or similar will need to be
+updated.
+
Do not lookup ``__buffer__`` attribute in `numpy.frombuffer`
------------------------------------------------------------
Looking up ``__buffer__`` attribute in `numpy.frombuffer` was undocumented and
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index a5ae2eeed..a3d4c6efb 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -948,7 +948,7 @@ class TestGradient(object):
assert_equal(type(out), type(x))
# And make sure that the output and input don't have aliased mask
# arrays
- assert_(x.mask is not out.mask)
+ assert_(x._mask is not out._mask)
# Also check that edge_order=2 doesn't alter the original mask
x2 = np.ma.arange(5)
x2[2] = np.ma.masked
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index 7ed099722..93eb4d87a 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -3449,7 +3449,9 @@ class MaskedArray(ndarray):
# We could try to force a reshape, but that wouldn't work in some
# cases.
- return self._mask
+ # Return a view so that the dtype and shape cannot be changed in place
+ # This still preserves nomask by identity
+ return self._mask.view()
@mask.setter
def mask(self, value):
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index 1f80ba26d..fb3f1a810 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -374,12 +374,12 @@ class TestMaskedArray(object):
y2a = array(x1, mask=m, copy=1)
assert_(y2a._data.__array_interface__ != x1.__array_interface__)
- #assert_( y2a.mask is not m)
+ #assert_( y2a._mask is not m)
assert_(y2a._mask.__array_interface__ != m.__array_interface__)
assert_(y2a[2] is masked)
y2a[2] = 9
assert_(y2a[2] is not masked)
- #assert_( y2a.mask is not m)
+ #assert_( y2a._mask is not m)
assert_(y2a._mask.__array_interface__ != m.__array_interface__)
assert_(allequal(y2a.mask, 0))
@@ -5203,3 +5203,10 @@ def test_fieldless_void():
mx = np.ma.array(x, mask=x)
assert_equal(mx.dtype, x.dtype)
assert_equal(mx.shape, x.shape)
+
+
+def test_mask_shape_assignment_does_not_break_masked():
+ a = np.ma.masked
+ b = np.ma.array(1, mask=a.mask)
+ b.shape = (1,)
+ assert_equal(a.mask.shape, ())
diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py
index 07bfb613f..1c523768d 100644
--- a/numpy/ma/tests/test_old_ma.py
+++ b/numpy/ma/tests/test_old_ma.py
@@ -270,7 +270,7 @@ class TestMa(object):
y1 = array(x1, mask=m)
assert_(y1._data is not x1)
assert_(allequal(x1, y1._data))
- assert_(y1.mask is m)
+ assert_(y1._mask is m)
y1a = array(y1, copy=0)
# For copy=False, one might expect that the array would just
@@ -280,19 +280,19 @@ class TestMa(object):
y1._mask.__array_interface__)
y2 = array(x1, mask=m3, copy=0)
- assert_(y2.mask is m3)
+ assert_(y2._mask is m3)
assert_(y2[2] is masked)
y2[2] = 9
assert_(y2[2] is not masked)
- assert_(y2.mask is m3)
+ assert_(y2._mask is m3)
assert_(allequal(y2.mask, 0))
y2a = array(x1, mask=m, copy=1)
- assert_(y2a.mask is not m)
+ assert_(y2a._mask is not m)
assert_(y2a[2] is masked)
y2a[2] = 9
assert_(y2a[2] is not masked)
- assert_(y2a.mask is not m)
+ assert_(y2a._mask is not m)
assert_(allequal(y2a.mask, 0))
y3 = array(x1 * 1.0, mask=m)
@@ -318,14 +318,14 @@ class TestMa(object):
assert_(x[3] is masked)
assert_(x[4] is masked)
x[[1, 4]] = [10, 40]
- assert_(x.mask is m)
+ assert_(x._mask is m)
assert_(x[3] is masked)
assert_(x[4] is not masked)
assert_(eq(x, [0, 10, 2, -1, 40]))
x = array(d, mask=m2, copy=True)
x.put([0, 1, 2], [-1, 100, 200])
- assert_(x.mask is not m2)
+ assert_(x._mask is not m2)
assert_(x[3] is masked)
assert_(x[4] is masked)
assert_(eq(x, [-1, 100, 200, 0, 0]))