diff --git a/numpy/ma/core.py b/numpy/ma/core.py index fe092f55249a..f142532dbee2 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -6309,6 +6309,18 @@ def copy(self, *args, **kwargs): # precedent for this with `np.bool_` scalars. return self + def __setattr__(self, attr, value): + if not self.__has_singleton(): + # allow the singleton to be initialized + return super(MaskedConstant, self).__setattr__(attr, value) + elif self is self.__singleton: + raise AttributeError( + "attributes of {!r} are not writeable".format(self)) + else: + # duplicate instance - we can end up here from __array_finalize__, + # where we set the __class__ attribute + return super(MaskedConstant, self).__setattr__(attr, value) + masked = masked_singleton = MaskedConstant() masked_array = MaskedArray diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index d5622e4bb878..8c631d95d4da 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -4981,6 +4981,10 @@ class Sub(type(np.ma.masked)): pass assert_(a is not np.ma.masked) assert_not_equal(repr(a), 'masked') + def test_attributes_readonly(self): + assert_raises(AttributeError, setattr, np.ma.masked, 'shape', (1,)) + assert_raises(AttributeError, setattr, np.ma.masked, 'dtype', np.int64) + class TestMaskedWhereAliases(object):