8000 support pep 3118 format strings for ctypes objects with nontrivial sh… · python/cpython@d3d2363 · GitHub
[go: up one dir, main page]

Skip to content

Commit d3d2363

Browse files
committed
support pep 3118 format strings for ctypes objects with nontrivial shapes (closes #10744)
Patch from Matti Picus.
1 parent c2a66f2 commit d3d2363

File tree

5 files changed

+75
-15
lines changed

5 files changed

+75
-15
lines changed

Lib/ctypes/test/test_pep3118.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ class EmptyStruct(Structure):
9292
class aUnion(Union):
9393
_fields_ = [("a", c_int)]
9494

95+
class StructWithArrays(Structure):
96+
_fields_ = [("x", c_long * 3 * 2), ("y", Point * 4)]
97+
98+
9599
class Incomplete(Structure):
96100
pass
97101

@@ -141,10 +145,10 @@ class Complete(Structure):
141145

142146
## arrays and pointers
143147

144-
(c_double * 4, "(4)<d", (4,), c_double),
145-
(c_float * 4 * 3 * 2, "(2,3,4)<f", (2,3,4), c_float),
146-
(POINTER(c_short) * 2, "(2)&<h", (2,), POINTER(c_short)),
147-
(POINTER(c_short) * 2 * 3, "(3,2)&<h", (3,2,), POINTER(c_short)),
148+
(c_double * 4, "<d", (4,), c_double),
149+
(c_float * 4 * 3 * 2, "<f", (2,3,4), c_float),
150+
(POINTER(c_short) * 2, "&<h", (2,), POINTER(c_short)),
151+
(POINTER(c_short) * 2 * 3, "&<h", (3,2,), POINTER(c_short)),
148152
(POINTER(c_short * 2), "&(2)<h", None, POINTER(c_short)),
149153

150154
## structures and unions
@@ -156,6 +160,9 @@ class Complete(Structure):
156160
(EmptyStruct, "T{}", None, EmptyStruct),
157161
# the pep does't support unions
158162
(aUnion, "B", None, aUnion),
163+
# structure with sub-arrays
164+
(StructWithArrays, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", None, StructWithArrays),
165+
(StructWithArrays * 3, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (3,), StructWithArrays),
159166

160167
## pointer to incomplete structure
161168
(Incomplete, "B", None, Incomplete),

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ Core and Builtins
4949
Library
5050
-------
5151

52+
- Issue #10744: Fix PEP 3118 format strings on ctypes objects with a nontrivial
53+
shape.
54+
5255
- Issue #7776: Backport Fix ``Host:'' header and reconnection when using
5356
http.client.HTTPConnection.set_tunnel() from Python 3. Patch by Nikolaus
5457
Rath.

Modules/_ctypes/_ctypes.c

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,48 @@ _ctypes_alloc_format_string(const char *prefix, const char *suffix)
321321
return result;
322322
}
323323

324+
/*
325+
Allocate a memory block for a pep3118 format string, adding
326+
the given prefix (if non-null), an additional shape prefix, and a suffix.
327+
Returns NULL on failure, with the error indicator set. If called with
328+
a suffix of NULL the error indicator must already be set.
329+
*/
330+
char *
331+
_ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape,
332+
const char *prefix, const char *suffix)
333+
{
334+
char *new_prefix;
335+
char *result;
336+
char buf[32];
337+
int prefix_len;
338+
int k;
339+
340+
prefix_len = 32 * ndim + 3;
341+
if (prefix)
342+
prefix_len += strlen(prefix);
343+
new_prefix = PyMem_Malloc(prefix_len);
344+
if (new_prefix == NULL)
345+
return NULL;
346+
new_prefix[0] = '\0';
347+
if (prefix)
348+
strcpy(new_prefix, prefix);
349+
if (ndim > 0) {
350+
/* Add the prefix "(shape[0],shape[1],...,shape[ndim-1])" */
351+
strcat(new_prefix, "(");
352+
for (k = 0; k < ndim; ++k) {
353+
if (k < ndim-1) {
354+
sprintf(buf, "%"PY_FORMAT_SIZE_T"d,", shape[k]);
355+
} else {
356+
sprintf(buf, "%"PY_FORMAT_SIZE_T"d)", shape[k]);
357+
}
358+
strcat(new_prefix, buf);
359+
}
360+
}
361+
result = _ctypes_alloc_format_string(new_prefix, suffix);
362+
PyMem_Free(new_prefix);
363+
return result;
364+
}
365+
324366
/*
325367
PyCStructType_Type - a meta type/class. Creating a new class using this one as
326368
__metaclass__ will call the contructor StructUnionType_new. It replaces the
@@ -917,14 +959,21 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
917959

918960
if (proto) {
919961
StgDictObject *itemdict = PyType_stgdict(proto);
962+
const char *current_format;
920963
assert(itemdict);
921964
/* If itemdict->format is NULL, then this is a pointer to an
922965
incomplete type. We create a generic format string
923966
'pointer to bytes' in this case. XXX Better would be to
924967
fix the format string later...
925968
*/
926-
stgdict->format = _ctypes_alloc_format_string("&",
927-
itemdict->format ? itemdict->format : "B");
969+
current_format = itemdict->format ? itemdict->format : "B";
970+
if (itemdict->shape != NULL) {
971+
/* pointer to an array: the shape needs to be prefixed */
972+
stgdict->format = _ctypes_alloc_format_string_with_shape(
973+
itemdict->ndim, itemdict->shape, "&", current_format);
974+
} else {
975+
stgdict->format = _ctypes_alloc_format_string("&", current_format);
976+
}
928977
if (stgdict->format == NULL) {
929978
Py_DECREF((PyObject *)stgdict);
930979
return NULL;
@@ -1326,7 +1375,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
13261375
long length;
13271376

13281377
Py_ssize_t itemsize, itemalign;
1329-
char buf[32];
13301378

13311379
typedict = PyTuple_GetItem(args, 2);
13321380
if (!typedict)
@@ -1362,13 +1410,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
13621410
}
13631411

13641412
assert(itemdict->format);
1365-
if (itemdict->format[0] == '(') {
1366-
sprintf(buf, "(%ld,", length);
1367-
stgdict->format = _ctypes_alloc_format_string(buf, itemdict->format+1);
1368-
} else {
1369-
sprintf(buf, "(%ld)", length);
1370-
stgdict->format = _ctypes_alloc_format_string(buf, itemdict->format);
1371-
}
1413+
stgdict->format = _ctypes_alloc_format_string(NULL, itemdict->format);
13721414
if (stgdict->format == NULL) {
13731415
Py_DECREF((PyObject *)stgdict);
13741416
return NULL;

Modules/_ctypes/ctypes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ extern void _ctypes_add_traceback(char *, char *, int);
434434

435435
extern PyObject *PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr);
436436
extern char *_ctypes_alloc_format_string(const char *prefix, const char *suffix);
437+
extern char *_ctypes_alloc_format_string_with_shape(int ndim,
438+
const Py_ssize_t *shape,
439+
const char *prefix, const char *suffix);
437440

438441
extern int _ctypes_simple_instance(PyObject *obj);
439442

Modules/_ctypes/stgdict.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,12 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
518518
sprintf(buf, "%s:%s:", fieldfmt, fieldname);
519519

520520
ptr = stgdict->format;
521-
stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf);
521+
if (dict->shape != NULL) {
522+
stgdict->format = _ctypes_alloc_format_string_with_shape(
523+
dict->ndim, dict->shape, stgdict->format, buf);
524+
} else {
525+
stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf);
526+
}
522527
PyMem_Free(ptr);
523528
PyMem_Free(buf);
524529

0 commit comments

Comments
 (0)
0