8000 Merge pull request #14393 from ahaldane/fix_fieldless_view_itemsize · numpy/numpy@f786041 · GitHub
[go: up one dir, main page]

Skip to content

Commit f786041

Browse files
authored
Merge pull request #14393 from ahaldane/fix_fieldless_view_itemsize
BUG: view with fieldless dtype should raise if itemsize != 0
2 parents 495d352 + d6f7524 commit f786041

File tree

7 files changed

+63
-9
lines changed

7 files changed

+63 8000
-9
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PyDataType_ISUNSIZED(descr) now returns False for structured datatypes
2+
----------------------------------------------------------------------
3+
Previously this returned True for any datatype of itemsize 0, but now this
4+
returns false for the non-flexible datatype with itemsize 0, ``np.dtype([])``.
5+

doc/source/reference/c-api/array.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,10 @@ argument must be a :c:type:`PyObject *<PyObject>` that can be directly interpret
997997
called on flexible dtypes. Types that are attached to an array will always
998998
be sized, hence the array form of this macro not existing.
999999
1000+
.. versionchanged:: 1.18
1001+
1002+
For structured datatypes with no fields this function now returns False.
1003+
10001004
.. c:function:: PyTypeNum_ISUSERDEF(num)
10011005
10021006
.. c:function:: PyDataType_ISUSERDEF(descr)

numpy/core/include/numpy/ndarraytypes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1695,7 +1695,8 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags)
16951695
#define PyDataType_ISOBJECT(obj) PyTypeNum_ISOBJECT(((PyArray_Descr*)(obj))->type_num)
16961696
#define PyDataType_HASFIELDS(obj) (((PyArray_Descr *)(obj))->names != NULL)
16971697
#define PyDataType_HASSUBARRAY(dtype) ((dtype)->subarray != NULL)
1698-
#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0)
1698+
#define PyDataType_ISUNSIZED(dtype) ((dtype)->elsize == 0 && \
1699+
!PyDataType_HASFIELDS(dtype))
16991700
#define PyDataType_MAKEUNSIZED(dtype) ((dtype)->elsize = 0)
17001701

17011702
#define PyArray_ISBOOL(obj) PyTypeNum_ISBOOL(PyArray_TYPE(obj))

numpy/core/src/multiarray/arrayobject.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,15 +1200,28 @@ _void_compare(PyArrayObject *self, PyArrayObject *other, int cmp_op)
12001200
}
12011201
}
12021202
if (res == NULL && !PyErr_Occurred()) {
1203-
PyErr_SetString(PyExc_ValueError, "No fields found.");
1203+
/* these dtypes had no fields. Use a MultiIter to broadcast them
1204+
* to an output array, and fill with True (for EQ)*/
1205+
PyArrayMultiIterObject *mit = (PyArrayMultiIterObject *)
1206+
PyArray_MultiIterNew(2, self, other);
1207+
if (mit == NULL) {
1208+
return NULL;
1209+
}
1210+
1211+
res = PyArray_NewFromDescr(&PyArray_Type,
1212+
PyArray_DescrFromType(NPY_BOOL),
1213+
mit->nd, mit->dimensions,
1214+
NULL, NULL, 0, NULL);
1215+
Py_DECREF(mit);
1216+
if (res) {
1217+
PyArray_FILLWBYTE((PyArrayObject *)res,
1218+
cmp_op == Py_EQ ? 1 : 0);
1219+
}
12041220
}
12051221
return res;
12061222
}
12071223
else {
1208-
/*
1209-
* compare as a string. Assumes self and
1210-
* other have same descr->type
1211-
*/
1224+
/* compare as a string. Assumes self and other have same descr->type */
12121225
return _strings_richcompare(self, other, cmp_op, 0);
12131226
}
12141227
}

numpy/core/src/multiarray/ctors.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3843,7 +3843,13 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type,
38433843
s = (npy_intp)ts - offset;
38443844
n = (npy_intp)count;
38453845
itemsize = type->elsize;
3846-
if (n < 0 ) {
3846+
if (n < 0) {
3847+
if (itemsize == 0) {
3848+
PyErr_SetString(PyExc_ValueError,
3849+
"cannot determine count if itemsize is 0");
3850+
Py_DECREF(type);
3851+
return NULL;
3852+
}
38473853
if (s % itemsize != 0) {
38483854
PyErr_SetString(PyExc_ValueError,
38493855
"buffer size must be a multiple"\
@@ -4036,7 +4042,7 @@ PyArray_FromIter(PyObject *obj, PyArray_Descr *dtype, npy_intp count)
40364042
}
40374043
for (i = 0; (i < count || count == -1) &&
40384044
(value = PyIter_Next(iter)); i++) {
4039-
if (i >= elcount) {
4045+
if (i >= elcount && elsize != 0) {
40404046
npy_intp nbytes;
40414047
/*
40424048
Grow PyArray_DATA(ret):

numpy/core/src/multiarray/methods.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1861,7 +1861,7 @@ array_reduce_ex(PyArrayObject *self, PyObject *args)
18611861
PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT) ||
18621862
(PyType_IsSubtype(((PyObject*)self)->ob_type, &PyArray_Type) &&
18631863
((PyObject*)self)->ob_type != &PyArray_Type) ||
1864-
PyDataType_ISUNSIZED(descr)) {
1864+
descr->elsize == 0) {
18651865
/* The PickleBuffer class from version 5 of the pickle protocol
18661866
* can only be used for arrays backed by a contiguous data buffer.
18671867
* For all other cases we fallback to the generic array_reduce

numpy/core/tests/test_dtype.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,31 @@ def test_partial_dict(self):
419419
assert_raises(ValueError, np.dtype,
420420
{'formats': ['i4', 'i4'], 'f0': ('i4', 0), 'f1':('i4', 4)})
421421

422+
def test_fieldless_views(self):
423+
a = np.zeros(2, dtype={'names':[], 'formats':[], 'offsets':[],
424+
'itemsize':8})
425+
assert_raises(ValueError, a.view, np.dtype([]))
426+
427+
d = np.dtype((np.dtype([]), 10))
428+
assert_equal(d.shape, (10,))
429+
assert_equal(d.itemsize, 0)
430+
assert_equal(d.base, np.dtype([]))
431+
432+
arr = np.fromiter((() for i in range(10)), [])
433+
assert_equal(arr.dtype, np.dtype([]))
434+
assert_raises(ValueError, np.frombuffer, b'', dtype=[])
435+
assert_equal(np.frombuffer(b'', dtype=[], count=2),
436+
np.empty(2, dtype=[]))
437+
438+
assert_raises(ValueError, np.dtype, ([], 'f8'))
439+
assert_raises(ValueError, np.zeros(1, dtype='i4').view, [])
440+
441+
assert_equal(np.zeros(2, dtype=[]) == np.zeros(2, dtype=[]),
442+
np.ones(2, dtype=bool))
443+
444+
assert_equal(np.zeros((1, 2), dtype=[]) == a,
445+
np.ones((1, 2), dtype=bool))
446+
422447

423448
class TestSubarray(object):
424449
def test_single_subarray(self):

0 commit comments

Comments
 (0)
0