Skip to content

Commit 29b3c32

Browse files
committed
bpo-32782: PEP3118 itemsize of an empty ctypes array should not be 0
The itemsize returned in a memoryview of a ctypes array should be computed from the item type, not by dividing the total size by the length and assuming that the length is not zero.
1 parent c1e46e9 commit 29b3c32

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

Lib/ctypes/test/test_pep3118.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ class Complete(Structure):
176176
## arrays and pointers
177177

178178
(c_double * 4, "<d", (4,), c_double),
179+
(c_double * 0, "<d", (0,), c_double),
179180
(c_float * 4 * 3 * 2, "<f", (2,3,4), c_float),
181+
(c_float * 4 * 0 * 2, "<f", (2,0,4), c_float),
180182
(POINTER(c_short) * 2, "&<" + s_short, (2,), POINTER(c_short)),
181183
(POINTER(c_short) * 2 * 3, "&<" + s_short, (3,2,), POINTER(c_short)),
182184
(POINTER(c_short * 2), "&(2)<" + s_short, (), POINTER(c_short)),
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``ctypes`` arrays of length 0 now report a correct itemsize when a
2+
``memoryview`` is constructed from them, rather than always giving a value
3+
of 0.

Modules/_ctypes/_ctypes.c

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,10 +2596,45 @@ static PyMemberDef PyCData_members[] = {
25962596
{ NULL },
25972597
};
25982598

2599+
/*
2600+
Get the StgDictObject corresponding to a single item of a multidimensional
2601+
array.
2602+
Takes and returns a borrowed reference.
2603+
*/
2604+
static StgDictObject *PyCData_item_stgdict(StgDictObject *dict)
2605+
{
2606+
if (dict->ndim == 0) {
2607+
/* scalar is its own item */
2608+
return dict;
2609+
}
2610+
else {
2611+
/* follow _type_, eliminating a dimension */
2612+
PyObject *type_attr;
2613+
StgDictObject *item_dict;
2614+
2615+
type_attr = PyDict_GetItemString((PyObject *)dict, "_type_");
2616+
if (!type_attr) {
2617+
PyErr_SetString(PyExc_AttributeError,
2618+
"class must define a '_type_' attribute");
2619+
return NULL;
2620+
}
2621+
2622+
item_dict = PyType_stgdict(type_attr);
2623+
if (!item_dict) {
2624+
PyErr_SetString(PyExc_TypeError,
2625+
"_type_ must have storage info");
2626+
return NULL;
2627+
}
2628+
2629+
return PyCData_item_stgdict(item_dict);
2630+
}
2631+
}
2632+
25992633
static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
26002634
{
26012635
CDataObject *self = (CDataObject *)myself;
26022636
StgDictObject *dict = PyObject_stgdict(myself);
2637+
StgDictObject *item_dict;
26032638
Py_ssize_t i;
26042639

26052640
if (view == NULL) return 0;
@@ -2613,12 +2648,11 @@ static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
26132648
view->format = dict->format ? dict->format : "B";
26142649
view->ndim = dict->ndim;
26152650
view->shape = dict->shape;
2616-
view->itemsize = self->b_size;
2617-
if (view->itemsize) {
2618-
for (i = 0; i < view->ndim; ++i) {
2619-
view->itemsize /= dict->shape[i];
2620-
}
2651+
item_dict = PyCData_item_stgdict(dict);
2652+
if (item_dict == NULL) {
2653+
return -1;
26212654
}
2655+
view->itemsize = item_dict->size;
26222656
view->strides = NULL;
26232657
view->suboffsets = NULL;
26242658
view->internal = NULL;

0 commit comments

Comments
 (0)