Skip to content

Commit

Permalink
bpo-32782: PEP3118 itemsize of an empty ctypes array should not be 0
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
eric-wieser committed May 20, 2018
1 parent c1e46e9 commit 29b3c32
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Lib/ctypes/test/test_pep3118.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ class Complete(Structure):
## arrays and pointers

(c_double * 4, "<d", (4,), c_double),
(c_double * 0, "<d", (0,), c_double),
(c_float * 4 * 3 * 2, "<f", (2,3,4), c_float),
(c_float * 4 * 0 * 2, "<f", (2,0,4), c_float),
(POINTER(c_short) * 2, "&<" + s_short, (2,), POINTER(c_short)),
(POINTER(c_short) * 2 * 3, "&<" + s_short, (3,2,), POINTER(c_short)),
(POINTER(c_short * 2), "&(2)<" + s_short, (), POINTER(c_short)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``ctypes`` arrays of length 0 now report a correct itemsize when a
``memoryview`` is constructed from them, rather than always giving a value
of 0.
44 changes: 39 additions & 5 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2596,10 +2596,45 @@ static PyMemberDef PyCData_members[] = {
{ NULL },
};

/*
Get the StgDictObject corresponding to a single item of a multidimensional
array.
Takes and returns a borrowed reference.
*/
static StgDictObject *PyCData_item_stgdict(StgDictObject *dict)
{
if (dict->ndim == 0) {
/* scalar is its own item */
return dict;
}
else {
/* follow _type_, eliminating a dimension */
PyObject *type_attr;
StgDictObject *item_dict;

type_attr = PyDict_GetItemString((PyObject *)dict, "_type_");
if (!type_attr) {
PyErr_SetString(PyExc_AttributeError,
"class must define a '_type_' attribute");
return NULL;
}

item_dict = PyType_stgdict(type_attr);
if (!item_dict) {
PyErr_SetString(PyExc_TypeError,
"_type_ must have storage info");
return NULL;
}

return PyCData_item_stgdict(item_dict);
}
}

static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
{
CDataObject *self = (CDataObject *)myself;
StgDictObject *dict = PyObject_stgdict(myself);
StgDictObject *item_dict;
Py_ssize_t i;

if (view == NULL) return 0;
Expand All @@ -2613,12 +2648,11 @@ static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
view->format = dict->format ? dict->format : "B";
view->ndim = dict->ndim;
view->shape = dict->shape;
view->itemsize = self->b_size;
if (view->itemsize) {
for (i = 0; i < view->ndim; ++i) {
view->itemsize /= dict->shape[i];
}
item_dict = PyCData_item_stgdict(dict);
if (item_dict == NULL) {
return -1;
}
view->itemsize = item_dict->size;
view->strides = NULL;
view->suboffsets = NULL;
view->internal = NULL;
Expand Down

0 comments on commit 29b3c32

Please sign in to comment.