8000 BUG: view with fieldless dtype should raise if itemsize != 0 by ahaldane · Pull Request #14393 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: view with fieldless dtype should raise if itemsize != 0 #14393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 8, 2019
Merged
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
5 changes: 5 additions & 0 deletions doc/release/upcoming_changes/14393.c_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PyDataType_ISUNSIZED(descr) now returns False for structured datatypes
----------------------------------------------------------------------
Previously this returned True for any datatype of itemsize 0, but now this
returns false for the non-flexible datatype with itemsize 0, ``np.dtype([])``.

4 changes: 4 additions & 0 deletions doc/source/reference/c-api/array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,10 @@ argument must be a :c:type:`PyObject *<PyObject>` that can be directly interpret
called on flexible dtypes. Types that are attached to an array will always
be sized, hence the array form of this macro not existing.

.. versionchanged:: 1.18

For structured datatypes with no fields this function now returns False.

.. c:function:: PyTypeNum_ISUSERDEF(num)

.. c:function:: PyDataType_ISUSERDEF(descr)
Expand Down
3 changes: 2 additions & 1 deletion numpy/core/include/numpy/ndarraytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1695,7 +1695,8 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags)
#define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num)
#define PyDataType_HASFIELDS(obj) (((PyArray_Descr *)(obj))->names != NULL)
#define PyDataType_HASSUBARRAY(dtype) ((dtype)->subarray != NULL)
#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0)
#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \
!PyDataType_HASFIELDS(dtype))
#define PyDataType_MAKEUNSIZED(dtype) ((dtype)->elsize = 0)

#define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj))
Expand Down
23 changes: 18 additions & 5 deletions numpy/core/src/multiarray/arrayobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1200,15 +1200,28 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op)
}
}
if (res == NULL && !PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, "No fields found.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes gh-13438, I think

/* these dtypes had no fields. Use a MultiIter to broadcast them
* to an output array, and fill with True (for EQ)*/
PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)
PyArray_MultiIterNew(2, self, other);
if (mit == NULL) {
return NULL;
}

res = PyArray_NewFromDescr(&PyArray_Type,
PyArray_DescrFromType(NPY_BOOL),
mit->nd, mit->dimensions,
NULL, NULL, 0, NULL);
Py_DECREF(mit);
if (res) {
PyArray_FILLWBYTE((PyArrayObject *)res,
cmp_op == Py_EQ ? 1 : 0);
}
}
return res;
}
else {
/*
* compare as a string. Assumes self and
* other have same descr->type
*/
/* compare as a string. Assumes self and other have same descr->type */
return _strings_richcompare(self, other, cmp_op, 0);
}
}
Expand Down
10 changes: 8 additions & 2 deletions numpy/core/src/multiarray/ctors.c
Original file line number Diff line number Diff line change
Expand Up @@ -3843,7 +3843,13 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
s = (npy_intp)ts - offset;
n = (npy_intp)count;
itemsize = type->elsize;
if (n < 0 ) {
if (n < 0) {
if (itemsize == 0) {
PyErr_SetString(PyExc_ValueError,
"cannot determine count if itemsize is 0");
Py_DECREF(type);
return NULL;
}
if (s % itemsize != 0) {
PyErr_SetString(PyExc_ValueError,
"buffer size must be a multiple"\
Expand Down Expand Up @@ -4036,7 +4042,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
}
for (i = 0; (i < count || count == -1) &&
(value = PyIter_Next(iter)); i++) {
if (i >= elcount) {
if (i >= elcount && elsize != 0) {
npy_intp nbytes;
/*
Grow PyArray_DATA(ret):
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/src/multiarray/methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -1861,7 +1861,7 @@ array_reduce_ex(PyArrayObject *self, PyObject *args)
PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT) ||
(PyType_IsSubtype(((PyObject*)self)->ob_type, &PyArray_Type) &&
((PyObject*)self)->ob_type != &PyArray_Type) ||
PyDataType_ISUNSIZED(descr)) {
descr->elsize == 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why this condition is needed at all, although it admittedly looks harmless.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not clear to me either. These lines previously discussed here: https://github.com/numpy/numpy/pull/12748/files#r249258808 and introduced in #12011, but not discussed there.

/* The PickleBuffer class from version 5 of the pickle protocol
* can only be used for arrays backed by a contiguous data buffer.
* For all other cases we fallback to the generic array_reduce
Expand Down
25 changes: 25 additions & 0 deletions num A3E2 py/core/tests/test_dtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,31 @@ def test_partial_dict(self):
assert_raises(ValueError, np.dtype,
{'formats': ['i4', 'i4'], 'f0': ('i4', 0), 'f1':('i4', 4)})

def test_fieldless_views(self):
a = np.zeros(2, dtype={'names':[], 'formats':[], 'offsets':[],
'itemsize':8})
assert_raises(ValueError, a.view, np.dtype([]))

d = np.dtype((np.dtype([]), 10))
assert_equal(d.shape, (10,))
assert_equal(d.itemsize, 0)
assert_equal(d.base, np.dtype([]))

arr = np.fromiter((() for i in range(10)), [])
assert_equal(arr.dtype, np.dtype([]))
assert_raises(ValueError, np.frombuffer, b'', dtype=[])
assert_equal(np.frombuffer(b'', dtype=[], count=2),
np.empty(2, dtype=[]))

assert_raises(ValueError, np.dtype, ([], 'f8'))
assert_raises(ValueError, np.zeros(1, dtype='i4').view, [])

assert_equal(np.zeros(2, dtype=[]) == np.zeros(2, dtype=[]),
np.ones(2, dtype=bool))

assert_equal(np.zeros((1, 2), dtype=[]) == a,
np.ones((1, 2), dtype=bool))


class TestSubarray(object):
def test_single_subarray(self):
Expand Down
0