From f4e3ca9ad1735fba4f85d828ed4e99f6ac8557df Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Tue, 6 Feb 2018 22:58:25 -0800 Subject: [PATCH 1/2] 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. --- Lib/ctypes/test/test_pep3118.py | 2 + .../2018-02-06-23-21-13.bpo-32782.EJVSfR.rst | 3 ++ Modules/_ctypes/_ctypes.c | 49 ++++++++++++++++--- 3 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-02-06-23-21-13.bpo-32782.EJVSfR.rst diff --git a/Lib/ctypes/test/test_pep3118.py b/Lib/ctypes/test/test_pep3118.py index 81e8ca7638fdeb..efffc80a66fcb8 100644 --- a/Lib/ctypes/test/test_pep3118.py +++ b/Lib/ctypes/test/test_pep3118.py @@ -176,7 +176,9 @@ class Complete(Structure): ## arrays and pointers (c_double * 4, "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); - Py_ssize_t i; + StgDictObject *item_dict; if (view == NULL) return 0; @@ -2613,12 +2649,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; From dd2069f5c9ca3856389a8f48c04db70ebda1d6b8 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 6 Jun 2019 20:20:34 -0700 Subject: [PATCH 2/2] bpo-32782: Avoid touching python attributes when the C attribute is available. --- Modules/_ctypes/_ctypes.c | 50 +++++++++++++-------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index fc3c6c8df55041..ea4d0f9f936b07 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2596,38 +2596,23 @@ 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) +/* Find the innermost type of an array type, returning a borrowed reference */ +static PyObject * +PyCData_item_type(PyObject *type) { - if (dict->ndim == 0) { - /* scalar is its own item */ - return dict; + if (PyCArrayTypeObject_Check(type)) { + StgDictObject *stg_dict; + PyObject *elem_type; + + /* asserts used here as these are all guaranteed by construction */ + stg_dict = PyType_stgdict(type); + assert(stg_dict); + elem_type = stg_dict->proto; + assert(elem_type); + return PyCData_item_type(elem_type); } 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); + return type; } } @@ -2636,7 +2621,8 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) { CDataObject *self = (CDataObject *)myself; StgDictObject *dict = PyObject_stgdict(myself); - StgDictObject *item_dict; + PyObject *item_type = PyCData_item_type((PyObject*)Py_TYPE(myself)); + StgDictObject *item_dict = PyType_stgdict(item_type); if (view == NULL) return 0; @@ -2649,10 +2635,6 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) view->format = dict->format ? dict->format : "B"; view->ndim = dict->ndim; view->shape = dict->shape; - item_dict = PyCData_item_stgdict(dict); - if (item_dict == NULL) { - return -1; - } view->itemsize = item_dict->size; view->strides = NULL; view->suboffsets = NULL;