Skip to content

Commit 6ccb03d

Browse files
authored
Merge pull request numpy#10314 from mhvk/ma-array-finalize-mask-view
BUG: Ensure __array_finalize__ cannot back-mangle shape
2 parents 2963517 + b779bae commit 6ccb03d

File tree

4 files changed

+45
-9
lines changed

4 files changed

+45
-9
lines changed

numpy/ma/core.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,7 +2992,9 @@ def __array_finalize__(self, obj):
29922992
order = "K"
29932993

29942994
_mask = _mask.astype(_mask_dtype, order)
2995-
2995+
else:
2996+
# Take a view so shape changes, etc., do not propagate back.
2997+
_mask = _mask.view()
29962998
else:
29972999
_mask = nomask
29983000

@@ -3337,17 +3339,35 @@ def __setitem__(self, indx, value):
33373339
_mask[indx] = mindx
33383340
return
33393341

3340-
def __setattr__(self, attr, value):
3341-
super(MaskedArray, self).__setattr__(attr, value)
3342-
if attr == 'dtype' and self._mask is not nomask:
3343-
self._mask = self._mask.view(make_mask_descr(value), ndarray)
3344-
# Try to reset the shape of the mask (if we don't have a void)
3345-
# This raises a ValueError if the dtype change won't work
3342+
# Define so that we can overwrite the setter.
3343+
@property
3344+
def dtype(self):
3345+
return super(MaskedArray, self).dtype
3346+
3347+
@dtype.setter
3348+
def dtype(self, dtype):
3349+
super(MaskedArray, type(self)).dtype.__set__(self, dtype)
3350+
if self._mask is not nomask:
3351+
self._mask = self._mask.view(make_mask_descr(dtype), ndarray)
3352+
# Try to reset the shape of the mask (if we don't have a void).
3353+
# This raises a ValueError if the dtype change won't work.
33463354
try:
33473355
self._mask.shape = self.shape
33483356
except (AttributeError, TypeError):
33493357
pass
33503358

3359+
@property
3360+
def shape(self):
3361+
return super(MaskedArray, self).shape
3362+
3363+
@shape.setter
3364+
def shape(self, shape):
3365+
super(MaskedArray, type(self)).shape.__set__(self, shape)
3366+
# Cannot use self._mask, since it may not (yet) exist when a
3367+
# masked matrix sets the shape.
3368+
if getmask(self) is not nomask:
3369+
self._mask.shape = self.shape
3370+
33513371
def __setmask__(self, mask, copy=False):
33523372
"""
33533373
Set the mask.

numpy/ma/tests/test_core.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,11 @@ def test_copy(self):
352352
assert_equal(y1._mask.__array_interface__, m.__array_interface__)
353353

354354
y1a = array(y1)
355+
# Default for masked array is not to copy; see gh-10318.
355356
assert_(y1a._data.__array_interface__ ==
356357
y1._data.__array_interface__)
357-
assert_(y1a.mask is y1.mask)
358+
assert_(y1a._mask.__array_interface__ ==
359+
y1._mask.__array_interface__)
358360

359361
y2 = array(x1, mask=m3)
360362
assert_(y2._data.__array_interface__ == x1.__array_interface__)

numpy/ma/tests/test_old_ma.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,11 @@ def test_testCopySize(self):
273273
assert_(y1.mask is m)
274274

275275
y1a = array(y1, copy=0)
276-
assert_(y1a.mask is y1.mask)
276+
# For copy=False, one might expect that the array would just
277+
# passed on, i.e., that it would be "is" instead of "==".
278+
# See gh-4043 for discussion.
279+
assert_(y1a._mask.__array_interface__ ==
280+
y1._mask.__array_interface__)
277281

278282
y2 = array(x1, mask=m3, copy=0)
279283
assert_(y2.mask is m3)

numpy/ma/tests/test_regression.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,13 @@ def test_ddof_corrcoef(self):
7474
r1 = np.ma.corrcoef(x, y, ddof=1)
7575
# ddof should not have an effect (it gets cancelled out)
7676
assert_allclose(r0.data, r1.data)
77+
78+
def test_mask_not_backmangled(self):
79+
# See gh-10314. Test case taken from gh-3140.
80+
a = np.ma.MaskedArray([1., 2.], mask=[False, False])
81+
assert_(a.mask.shape == (2,))
82+
b = np.tile(a, (2, 1))
83+
# Check that the above no longer changes a.shape to (1, 2)
84+
assert_(a.mask.shape == (2,))
85+
assert_(b.shape == (2, 2))
86+
assert_(b.mask.shape == (2, 2))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy