diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/src/multiarray/descriptor.c | 137 | ||||
-rw-r--r-- | numpy/core/tests/test_dtype.py | 33 |
2 files changed, 86 insertions, 84 deletions
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index 702329824..c611a1402 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -818,10 +818,10 @@ _convert_from_dict(PyObject *obj, int align) { PyArray_Descr *new; PyObject *fields = NULL; - PyObject *names, *offsets, *descrs, *titles; + PyObject *names, *offsets, *descrs, *titles, *tmp; PyObject *metadata; int n, i; - int totalsize; + int totalsize, itemsize; int maxalign = 0; int dtypeflags = 0; int has_out_of_order_fields = 0; @@ -1007,6 +1007,37 @@ _convert_from_dict(PyObject *obj, int align) new->flags |= NPY_ALIGNED_STRUCT; } + /* Override the itemsize if provided */ + tmp = PyDict_GetItemString(obj, "itemsize"); + if (tmp != NULL) { + itemsize = (int)PyInt_AsLong(tmp); + if (itemsize == -1 && PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + /* Make sure the itemsize isn't made too small */ + if (itemsize < new->elsize) { + PyErr_Format(PyExc_ValueError, + "NumPy dtype descriptor requires %d bytes, " + "cannot override to smaller itemsize of %d", + (int)new->elsize, (int)itemsize); + Py_DECREF(new); + return NULL; + } + /* If align is set, make sure the alignment divides into the size */ + if (align && itemsize % new->alignment != 0) { + PyErr_Format(PyExc_ValueError, + "NumPy dtype descriptor requires alignment of %d bytes, " + "which is not divisible into the specified itemsize %d", + (int)new->alignment, (int)itemsize); + Py_DECREF(new); + return NULL; + } + /* Set the itemsize */ + new->elsize = itemsize; + } + + /* Add the metadata if provided */ metadata = PyDict_GetItemString(obj, "metadata"); if (new->metadata == NULL) { @@ -1873,26 +1904,23 @@ static PyObject * arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), PyObject *args, PyObject *kwds) { - PyObject *odescr, *ometadata=NULL; + PyObject *odescr, *metadata=NULL; PyArray_Descr *descr, *conv; npy_bool align = FALSE; npy_bool copy = FALSE; npy_bool copied = FALSE; - int itemsize = -1; - static char *kwlist[] = {"dtype", "align", "copy", "metadata", - "itemsize", NULL}; + static char *kwlist[] = {"dtype", "align", "copy", "metadata", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO&O&O!i", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO&O&O!", kwlist, &odescr, PyArray_BoolConverter, &align, PyArray_BoolConverter, ©, - &PyDict_Type, &ometadata, - &itemsize)) { + &PyDict_Type, &metadata)) { return NULL; } - if ((ometadata != NULL) && (_invalid_metadata_check(ometadata))) { + if ((metadata != NULL) && (_invalid_metadata_check(metadata))) { return NULL; } @@ -1913,7 +1941,7 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), copied = TRUE; } - if ((ometadata != NULL)) { + if ((metadata != NULL)) { /* * We need to be sure to make a new copy of the data-type and any * underlying dictionary @@ -1926,8 +1954,9 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), } if ((conv->metadata != NULL)) { /* - * Make a copy of the metadata before merging with ometadata - * so that this data-type descriptor has it's own copy + * Make a copy of the metadata before merging with the + * input metadata so that this data-type descriptor has + * it's own copy */ /* Save a reference */ odescr = conv->metadata; @@ -1939,55 +1968,14 @@ arraydescr_new(PyTypeObject *NPY_UNUSED(subtype), * Update conv->metadata with anything new in metadata * keyword, but do not over-write anything already there */ - if (PyDict_Merge(conv->metadata, ometadata, 0) != 0) { + if (PyDict_Merge(conv->metadata, metadata, 0) != 0) { Py_DECREF(conv); return NULL; } } else { /* Make a copy of the input dictionary */ - conv->metadata = PyDict_Copy(ometadata); - } - } - - /* Override the itemsize if provided */ - if (itemsize >= 0) { - /* Only can override the item size of structured arrays */ - if (!PyDataType_HASFIELDS(conv)) { - PyErr_Format(PyExc_ValueError, - "The itemsize parameter may only be used when " - "constructing a data type with fields"); - Py_DECREF(conv); - return NULL; - } - /* Make sure the itemsize isn't made too small */ - if (itemsize < conv->elsize) { - PyErr_Format(PyExc_ValueError, - "NumPy dtype descriptor requires %d bytes, " - "cannot override to smaller itemsize of %d", - (int)conv->elsize, (int)itemsize); - Py_DECREF(conv); - return NULL; - } - /* If align is set, make sure the alignment divides into the size */ - if (align && itemsize % conv->alignment != 0) { - PyErr_Format(PyExc_ValueError, - "NumPy dtype descriptor requires alignment of %d bytes, " - "which is not divisible into the specified itemsize %d", - (int)conv->alignment, (int)itemsize); - Py_DECREF(conv); - return NULL; - } - /* Change the itemsize */ - if (itemsize != conv->elsize) { - /* Make sure we can change the itemsize */ - if (!copied) { - copied = TRUE; - descr = PyArray_DescrNew(conv); - Py_DECREF(conv); - conv = descr; - } - conv->elsize = itemsize; + conv->metadata = PyDict_Copy(metadata); } } @@ -2809,12 +2797,17 @@ arraydescr_struct_dict_str(PyArray_Descr *dtype) Py_ssize_t i, names_size; PyArray_Descr *fld_dtype; int fld_offset, has_titles; + int align, naturalsize; names = dtype->names; names_size = PyTuple_GET_SIZE(names); fields = dtype->fields; has_titles = 0; + /* Used to determine whether the 'itemsize=' is needed */ + align = (dtype->flags&NPY_ALIGNED_STRUCT) != 0; + naturalsize = 0; + /* Build up a string to make the dictionary */ /* First, the names */ @@ -2865,6 +2858,10 @@ arraydescr_struct_dict_str(PyArray_Descr *dtype) if (i != names_size - 1) { PyUString_ConcatAndDel(&ret, PyUString_FromString(",")); } + /* Accumulate the natural size of the dtype */ + if (fld_offset + fld_dtype->elsize > naturalsize) { + naturalsize = fld_offset + fld_dtype->elsize; + } } /* Fourth, the titles */ if (has_titles) { @@ -2887,7 +2884,18 @@ arraydescr_struct_dict_str(PyArray_Descr *dtype) } } } - PyUString_ConcatAndDel(&ret, PyUString_FromString("]}")); + /* The alignment is always a power of 2, so this works */ + if (align) { + naturalsize = (naturalsize + dtype->alignment - 1) & (-dtype->alignment); + } + /* Finally, the itemsize */ + if (naturalsize == dtype->elsize) { + PyUString_ConcatAndDel(&ret, PyUString_FromString("]}")); + } + else { + PyUString_ConcatAndDel(&ret, + PyUString_FromFormat("], 'itemsize':%d}", (int)dtype->elsize)); + } return ret; } @@ -2947,17 +2955,9 @@ static PyObject * arraydescr_struct_repr(PyArray_Descr *dtype) { PyObject *sub, *s; - int is_simple; s = PyUString_FromString("dtype("); - if (is_dtype_struct_simple_layout(dtype)) { - sub = arraydescr_struct_list_str(dtype); - is_simple = 1; - } - else { - sub = arraydescr_struct_dict_str(dtype); - is_simple = 0; - } + sub = arraydescr_struct_str(dtype); if (sub == NULL) { return NULL; } @@ -2968,11 +2968,6 @@ arraydescr_struct_repr(PyArray_Descr *dtype) if (dtype->flags&NPY_ALIGNED_STRUCT) { PyUString_ConcatAndDel(&s, PyUString_FromString(", align=True")); } - /* If it wasn't simple, also specify the itemsize parameter */ - if (!is_simple) { - PyUString_ConcatAndDel(&s, - PyUString_FromFormat(", itemsize=%d", (int)dtype->elsize)); - } PyUString_ConcatAndDel(&s, PyUString_FromString(")")); return s; diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 772b23206..70046cb72 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -42,14 +42,18 @@ class TestBuiltin(TestCase): assert_raises(TypeError, np.dtype, typestr) def test_bad_param(self): - # Can't override the itemsize of a non-struct type - assert_raises(ValueError, np.dtype, 'f4', itemsize=6) - # Even if you specify the same size - assert_raises(ValueError, np.dtype, 'f4', itemsize=4) # Can't give a size that's too small - assert_raises(ValueError, np.dtype, 'f4, i4', itemsize=7) + assert_raises(ValueError, np.dtype, + {'names':['f0','f1'], + 'formats':['i4', 'i1'], + 'offsets':[0,4], + 'itemsize':4}) # If alignment is enabled, the alignment (4) must divide the itemsize - assert_raises(ValueError, np.dtype, 'f4, i1', align=True, itemsize=9) + assert_raises(ValueError, np.dtype, + {'names':['f0','f1'], + 'formats':['i4', 'i1'], + 'offsets':[0,4], + 'itemsize':9}, align=True) # If alignment is enabled, the individual fields must be aligned assert_raises(ValueError, np.dtype, {'names':['f0','f1'], @@ -71,10 +75,12 @@ class TestRecord(TestCase): def test_different_titles(self): # In theory, they may hash the same (collision) ? - a = np.dtype({'names': ['r','b'], 'formats': ['u1', 'u1'], - 'titles': ['Red pixel', 'Blue pixel']}) - b = np.dtype({'names': ['r','b'], 'formats': ['u1', 'u1'], - 'titles': ['RRed pixel', 'Blue pixel']}) + a = np.dtype({'names': ['r','b'], + 'formats': ['u1', 'u1'], + 'titles': ['Red pixel', 'Blue pixel']}) + b = np.dtype({'names': ['r','b'], + 'formats': ['u1', 'u1'], + 'titles': ['RRed pixel', 'Blue pixel']}) assert_dtype_not_equal(a, b) def test_not_lists(self): @@ -324,13 +330,14 @@ class TestString(TestCase): dt = np.dtype({'names': ['r','b'], 'formats': ['u1', 'u1'], 'offsets': [0, 2], - 'titles': ['Red pixel', 'Blue pixel']}, - itemsize = 4) + 'titles': ['Red pixel', 'Blue pixel'], + 'itemsize': 4}) assert_equal(repr(dt), "dtype({'names':['r','b'], " "'formats':['u1','u1'], " "'offsets':[0,2], " - "'titles':['Red pixel','Blue pixel']}, itemsize=4)") + "'titles':['Red pixel','Blue pixel'], " + "'itemsize':4})") if __name__ == "__main__": run_module_suite() |