Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions astropy/table/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -1239,13 +1239,6 @@ def _convert_data_to_col(self, data, copy=True, default_name=None, dtype=None, n
f'{fully_qualified_name} '
'did not return a valid mixin column')

# Structured ndarray gets viewed as a mixin unless already a valid
# mixin class
if (not isinstance(data, Column) and not data_is_mixin
and isinstance(data, np.ndarray) and len(data.dtype) > 1):
data = data.view(NdarrayMixin)
data_is_mixin = True

# Get the final column name using precedence. Some objects may not
# have an info attribute. Also avoid creating info as a side effect.
if not name:
Expand Down
121 changes: 87 additions & 34 deletions astropy/table/tests/test_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,8 @@ def test_skycoord_representation():


def test_ndarray_mixin():
"""
Test directly adding a plain structured array into a table instead of the
view as an NdarrayMixin. Once added as an NdarrayMixin then all the previous
tests apply.
"""
"""Structured ndarrays become plain Columns unless explicitly viewed."""

a = np.array([(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')],
dtype='<i4,' + ('|U1'))
b = np.array([(10, 'aa'), (20, 'bb'), (30, 'cc'), (40, 'dd')],
Expand All @@ -711,43 +708,27 @@ def test_ndarray_mixin():
names=['rx', 'ry'])
d = np.arange(8, dtype='i8').reshape(4, 2).view(NdarrayMixin)

# Add one during initialization and the next as a new column.
# Add one during initialization and the next as new columns.
t = Table([a], names=['a'])
t['b'] = b
t['c'] = c
t['d'] = d

assert isinstance(t['a'], NdarrayMixin)

assert t['a'][1][1] == a[1][1]
assert t['a'][2][0] == a[2][0]

assert t[1]['a'][1] == a[1][1]
assert t[2]['a'][0] == a[2][0]

assert isinstance(t['b'], NdarrayMixin)
for name, arr in (('a', a), ('b', b), ('c', c)):
col = t[name]
assert isinstance(col, Column)
assert col.dtype == arr.dtype
assert np.array_equal(col, arr)

assert t['b'][1]['x'] == b[1]['x']
assert t['b'][1]['y'] == b[1]['y']

assert t[1]['b']['x'] == b[1]['x']
assert t[1]['b']['y'] == b[1]['y']

assert isinstance(t['c'], NdarrayMixin)

assert t['c'][1]['rx'] == c[1]['rx']
assert t['c'][1]['ry'] == c[1]['ry']

assert t[1]['c']['rx'] == c[1]['rx']
assert t[1]['c']['ry'] == c[1]['ry']
# Row-first access keeps the structured dtype
row = t[1][name]
for field in arr.dtype.names:
assert col[1][field] == arr[field][1]
assert row[field] == arr[field][1]

# Explicit NdarrayMixin view still produces a mixin column
assert isinstance(t['d'], NdarrayMixin)

assert t['d'][1][0] == d[1][0]
assert t['d'][1][1] == d[1][1]

assert t[1]['d'][0] == d[1][0]
assert t[1]['d'][1] == d[1][1]
assert np.array_equal(t['d'], d)

assert t.pformat(show_dtype=True) == [
' a [f0, f1] b [x, y] c [rx, ry] d ',
Expand All @@ -759,6 +740,78 @@ def test_ndarray_mixin():
" (4, 'd') (40, 'dd') (400., 'rdd') 6 .. 7"]


def _structured_sample():
return np.array([(1, 2.0), (3, 4.5)], dtype=[('x', 'i4'), ('y', 'f8')])


def test_structured_add_column_becomes_column():
t = Table()
struct = _structured_sample()
t.add_column(struct, name='a')

assert isinstance(t['a'], Column)
assert t['a'].dtype == struct.dtype
assert np.array_equal(t['a'], struct)


def test_structured_setitem_becomes_column():
t = Table()
struct = _structured_sample()
t['b'] = struct

assert isinstance(t['b'], Column)
assert np.array_equal(t['b'], struct)


def test_structured_replace_column_becomes_column():
t = Table()
t['base'] = Column([10, 20], name='base')
struct = _structured_sample()

t.replace_column('base', struct)

assert isinstance(t['base'], Column)
assert t['base'].dtype == struct.dtype
assert np.array_equal(t['base'], struct)


def test_structured_constructor_list_of_columns():
a = _structured_sample()
b = np.array([(5, 'x'), (6, 'y')], dtype=[('col', 'i2'), ('label', 'U1')])

t = Table([a, b], names=['a', 'b'])

for name, arr in (('a', a), ('b', b)):
assert isinstance(t[name], Column)
assert t[name].dtype == arr.dtype
assert np.array_equal(t[name], arr)


def test_structured_constructor_splits_fields():
struct = np.array([(1, 2.0), (3, 4.5)], dtype=[('x', 'i4'), ('y', 'f8')])

t = Table(struct)

assert list(t.colnames) == ['x', 'y']
assert np.array_equal(t['x'], struct['x'])
assert np.array_equal(t['y'], struct['y'])


def test_structured_column_ecsv_roundtrip(tmp_path):
struct = _structured_sample()
t = Table()
t['a'] = struct

path = tmp_path / 'structured.ecsv'
t.write(path, format='ascii.ecsv', overwrite=True)

t2 = Table.read(path, format='ascii.ecsv')

assert isinstance(t2['a'], Column)
assert t2['a'].dtype == struct.dtype
assert np.array_equal(t2['a'], struct)


def test_possible_string_format_functions():
"""
The QuantityInfo info class for Quantity implements a
Expand Down
12 changes: 6 additions & 6 deletions cextern/cfitsio/lib/putcol.c
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,7 @@ int ffiter(int n_cols,
long rept, rowrept, width, tnull, naxes[9] = {1,1,1,1,1,1,1,1,1}, groups;
double zeros = 0.;
char message[FLEN_ERRMSG], keyname[FLEN_KEYWORD], nullstr[FLEN_VALUE];
char **stringptr, *nullptr, *cptr;
char **stringptr, *null_ptr, *cptr;

if (*status > 0)
return(*status);
Expand Down Expand Up @@ -1885,24 +1885,24 @@ int ffiter(int n_cols,
{
stringptr = cols[jj].array;
dataptr = stringptr + 1;
nullptr = *stringptr;
null_ptr = *stringptr;
nbytes = 2;
}
else
{
dataptr = (char *) cols[jj].array + col[jj].nullsize;
nullptr = (char *) cols[jj].array;
null_ptr = (char *) cols[jj].array;
nbytes = col[jj].nullsize;
}

if (memcmp(nullptr, &zeros, nbytes) )
if (memcmp(null_ptr, &zeros, nbytes) )
{
/* null value flag not zero; must check for and write nulls */
if (hdutype == IMAGE_HDU)
{
if (ffppn(cols[jj].fptr, cols[jj].datatype,
felement, cols[jj].repeat * ntodo, dataptr,
nullptr, &tstatus) > 0)
null_ptr, &tstatus) > 0)
break;
}
else
Expand All @@ -1917,7 +1917,7 @@ int ffiter(int n_cols,

if (ffpcn(cols[jj].fptr, cols[jj].datatype, cols[jj].colnum, frow,
felement, cols[jj].repeat * ntodo, dataptr,
nullptr, &tstatus) > 0)
null_ptr, &tstatus) > 0)
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
requires = ["setuptools",
"setuptools_scm>=6.2",
"wheel",
"cython==0.29.22",
"cython>=0.29.37,<0.30",
"oldest-supported-numpy",
"extension-helpers"]
build-backend = 'setuptools.build_meta'
Expand Down