astropy/astropy — pull request #2958 by mhvk (2014-09-19 13:26:56+00:00)
Title: doctest failure in time.core with --remote-data

I ran the tests in the v0.4.x branch with the `--remote-data` option and got one failure:

```
__________________________________ [doctest] astropy.time.core.Time.get_delta_ut1_utc __________________________________
825 To use an updated IERS A bulletin to calculate UT1-UTC
826 (see also ``astropy.utils.iers``)::
827
828 >>> from astropy.utils.iers import IERS_A, IERS_A_URL
829 >>> from astropy.utils.data import download_file
830 >>> t = Time(['1974-01-01', '2000-01-01'], scale='utc')
831 >>> iers_a_file = download_file(IERS_A_URL,
832 ... cache=True) # doctest: +REMOTE_DATA
833 Downloading ... [Done]
834 >>> iers_a = IERS_A.open(iers_a_file) # doctest: +REMOTE_DATA
UNEXPECTED EXCEPTION: AttributeError("'MaskedColumn' object has no attribute '_fill_value'",)
Traceback (most recent call last):

File "/internal/1/root/usr/local/lib/python2.7/doctest.py", line 1289, in __run
compileflags, 1) in test.globs

File "", line 1, in

File "astropy/utils/iers/iers.py", line 124, in open
cls.iers_table = cls.read(**kwargs)

File "astropy/utils/iers/iers.py", line 290, in read
return cls(iers_a[~iers_a['UT1_UTC_A'].mask])

File "astropy/utils/iers/iers.py", line 261, in __init__
table['UT1_UTC_B'])

File "astropy/table/table.py", line 792, in __setitem__
new_column = value.copy(copy_data=False)

File "astropy/table/column.py", line 160, in copy
data = self.data

File "astropy/table/column.py", line 790, in data
out.fill_value = self.fill_value

File "astropy/table/column.py", line 756, in fill_value
return self.get_fill_value() # defer to native ma.MaskedArray method

File "/internal/1/root/src/astropy/astropy/.tox/py27/lib/python2.7/site-packages/numpy/ma/core.py", line 3360, in get_fill_value
if self._fill_value is None:

AttributeError: 'MaskedColumn' object has no attribute '_fill_value'

astropy/time/core.py:834: UnexpectedException
```

For some reason this did not occur in Python 2.6 (though I need to double check this). It also does not occur in Python 3, but only because we don't run the doctests there. However, when I run the same test manually in Python 3.4 I get the same traceback.

*Update:* After further testing I can reduce this to a simpler case:

```
In [1]: import numpy as np

In [2]: class MyArray(np.ndarray):
def __new__(*args, **kwargs):
print('MyArray.__new__ called with', args, kwargs)
return super(MyArray, args[0]).__new__(*args, **kwargs)
def __array_finalize__(self, obj=None):
print('MyArray.__array_finalize__ called with', type(self), type(obj))
__array_priority__ = 1000
...:

In [3]: a = MyArray(3)
MyArray.__new__ called with (, 3) {}
MyArray.__array_finalize__ called with

In [4]: b = MyArray(3)
MyArray.__new__ called with (, 3) {}
MyArray.__array_finalize__ called with

In [5]: np.where([True, False, True], a, b)
MyArray.__array_finalize__ called with
Out[5]: MyArray([ 1.25665856e-312, 1.25665856e-312, 2.22248119e-317])
```

What this demonstrates is that for the output from the `np.where` call, Numpy creates a new `MyArray` without calling its `__new__`, and then calls its `__array_finalize__` without a template to copy from. In thinking about it this behavior seems intentional. It kind of makes sense. But if it is intentional Numpy's docs don't mention this mode of operation.

The solution, I think, is to set sensible defaults for the column's necessary attributes in `__array_finalize__` even when no template is given.