Skip to content

Commit cb9761d

Browse files
author
thomas.heller
committed
Merged revisions 60056-60071,60073-60127,60129-60261,60263-60284,60286-62589,62591-62594 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/py3k-ctypes-pep3118 ........ r60059 | thomas.heller | 2008-01-18 22:17:05 +0100 (Fri, 18 Jan 2008) | 1 line Implement pep3118 format strings for SimpleCData types. ........ r60108 | thomas.heller | 2008-01-19 22:56:12 +0100 (Sat, 19 Jan 2008) | 3 lines Always use explicit endian specifiers for simple types, and a bug fix too. Add unittest. ........ r60112 | thomas.heller | 2008-01-19 23:25:14 +0100 (Sat, 19 Jan 2008) | 2 lines Fully implement tp_asbuffer for pointer types. ........ r60261 | thomas.heller | 2008-01-24 22:01:29 +0100 (Thu, 24 Jan 2008) | 4 lines Added shape and ndim field to StgDictObject. Implemented pep3118 format string, ndim, and shape for array types. Added a buffer_info(type_or_object) for testing. ........ r60278 | thomas.heller | 2008-01-25 11:53:33 +0100 (Fri, 25 Jan 2008) | 2 lines Implement pep3118 format strings for ctypes.Structure and ctypes.Union. ........ r60288 | thomas.heller | 2008-01-25 17:58:30 +0100 (Fri, 25 Jan 2008) | 2 lines All ctypes types now use the same CData_GetBuffer function. ........ r60289 | thomas.heller | 2008-01-25 19:59:45 +0100 (Fri, 25 Jan 2008) | 2 lines Fix format string for structures, and itemsize for arrays. ........ r60290 | thomas.heller | 2008-01-25 20:09:03 +0100 (Fri, 25 Jan 2008) | 2 lines Implement to format string for function pointers. ........ r60292 | thomas.heller | 2008-01-25 20:32:20 +0100 (Fri, 25 Jan 2008) | 3 lines Only structures with native packing implement the pep. Unions, or packed structures do not. ........ r60293 | thomas.heller | 2008-01-25 20:34:31 +0100 (Fri, 25 Jan 2008) | 2 lines Update the test. ........ r60295 | thomas.heller | 2008-01-25 20:44:41 +0100 (Fri, 25 Jan 2008) | 2 lines Fixed a few XXX markers. ........ r60298 | thomas.heller | 2008-01-25 21:11:08 +0100 (Fri, 25 Jan 2008) | 1 line Fix test for 64-bt platform. ........ r60299 | thomas.heller | 2008-01-25 21:34:11 +0100 (Fri, 25 Jan 2008) | 2 lines Add test for the readonly bit. ........ r60384 | thomas.heller | 2008-01-28 08:45:04 +0100 (Mon, 28 Jan 2008) | 4 lines Restructure the test so that it contains little endian format strings. On big endian machines, the format strings are converted by replacing '<' with '>'. ........ r60385 | thomas.heller | 2008-01-28 08:58:46 +0100 (Mon, 28 Jan 2008) | 1 line Bugfix and test for explicit big and little endian types. ........ r60428 | thomas.heller | 2008-01-29 22:00:37 +0100 (Tue, 29 Jan 2008) | 1 line Add comments to clarify the tests. ........ r62589 | thomas.heller | 2008-04-30 13:49:46 +0200 (Wed, 30 Apr 2008) | 1 line Fix compiler warnings. ........ git-svn-id: http://svn.python.org/projects/python/branches/py3k@62597 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 61e6ecf commit cb9761d

7 files changed

Lines changed: 387 additions & 2 deletions

File tree

Lib/ctypes/test/test_pep3118.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import unittest
2+
from ctypes import *
3+
import re, struct, sys
4+
5+
if sys.byteorder == "little":
6+
THIS_ENDIAN = "<"
7+
OTHER_ENDIAN = ">"
8+
else:
9+
THIS_ENDIAN = ">"
10+
OTHER_ENDIAN = "<"
11+
12+
def normalize(format):
13+
# Remove current endian specifier and white space from a format
14+
# string
15+
format = format.replace(OTHER_ENDIAN, THIS_ENDIAN)
16+
return re.sub(r"\s", "", format)
17+
18+
class Test(unittest.TestCase):
19+
20+
def test_native_types(self):
21+
for tp, fmt, shape, itemtp in native_types:
22+
ob = tp()
23+
v = memoryview(ob)
24+
try:
25+
self.failUnlessEqual(normalize(v.format), normalize(fmt))
26+
self.failUnlessEqual(v.size, sizeof(ob))
27+
self.failUnlessEqual(v.itemsize, sizeof(itemtp))
28+
self.failUnlessEqual(v.shape, shape)
29+
# ctypes object always have a non-strided memory block
30+
self.failUnlessEqual(v.strides, None)
31+
# they are always read/write
32+
self.failIf(v.readonly)
33+
34+
if v.shape:
35+
n = 1
36+
for dim in v.shape:
37+
n = n * dim
38+
self.failUnlessEqual(v.itemsize * n, v.size)
39+
except:
40+
# so that we can see the failing type
41+
print(tp)
42+
raise
43+
44+
def test_endian_types(self):
45+
for tp, fmt, shape, itemtp in endian_types:
46+
ob = tp()
47+
v = memoryview(ob)
48+
try:
49+
self.failUnlessEqual(v.format, fmt)
50+
self.failUnlessEqual(v.size, sizeof(ob))
51+
self.failUnlessEqual(v.itemsize, sizeof(itemtp))
52+
self.failUnlessEqual(v.shape, shape)
53+
# ctypes object always have a non-strided memory block
54+
self.failUnlessEqual(v.strides, None)
55+
# they are always read/write
56+
self.failIf(v.readonly)
57+
58+
if v.shape:
59+
n = 1
60+
for dim in v.shape:
61+
n = n * dim
62+
self.failUnlessEqual(v.itemsize * n, v.size)
63+
except:
64+
# so that we can see the failing type
65+
print(tp)
66+
raise
67+
68+
# define some structure classes
69+
70+
class Point(Structure):
71+
_fields_ = [("x", c_long), ("y", c_long)]
72+
73+
class PackedPoint(Structure):
74+
_pack_ = 2
75+
_fields_ = [("x", c_long), ("y", c_long)]
76+
77+
class Point2(Structure):
78+
pass
79+
Point2._fields_ = [("x", c_long), ("y", c_long)]
80+
81+
class EmptyStruct(Structure):
82+
_fields_ = []
83+
84+
class aUnion(Union):
85+
_fields_ = [("a", c_int)]
86+
87+
################################################################
88+
#
89+
# This table contains format strings as they look on little endian
90+
# machines. The test replaces '<' with '>' on big endian machines.
91+
#
92+
native_types = [
93+
# type format shape calc itemsize
94+
95+
## simple types
96+
97+
(c_char, "<c", None, c_char),
98+
(c_byte, "<b", None, c_byte),
99+
(c_ubyte, "<B", None, c_ubyte),
100+
(c_short, "<h", None, c_short),
101+
(c_ushort, "<H", None, c_ushort),
102+
103+
# c_int and c_uint may be aliases to c_long
104+
#(c_int, "<i", None, c_int),
105+
#(c_uint, "<I", None, c_uint),
106+
107+
(c_long, "<l", None, c_long),
108+
(c_ulong, "<L", None, c_ulong),
109+
110+
# c_longlong and c_ulonglong are aliases on 64-bit platforms
111+
#(c_longlong, "<q", None, c_longlong),
112+
#(c_ulonglong, "<Q", None, c_ulonglong),
113+
114+
(c_float, "<f", None, c_float),
115+
(c_double, "<d", None, c_double),
116+
# c_longdouble may be an alias to c_double
117+
118+
(c_bool, "<?", None, c_bool),
119+
(py_object, "<O", None, py_object),
120+
121+
## pointers
122+
123+
(POINTER(c_byte), "&<b", None, POINTER(c_byte)),
124+
(POINTER(POINTER(c_long)), "&&<l", None, POINTER(POINTER(c_long))),
125+
126+
## arrays and pointers
127+
128+
(c_double * 4, "(4)<d", (4,), c_double),
129+
(c_float * 4 * 3 * 2, "(2,3,4)<f", (2,3,4), c_float),
130+
(POINTER(c_short) * 2, "(2)&<h", (2,), POINTER(c_short)),
131+
(POINTER(c_short) * 2 * 3, "(3,2)&<h", (3,2,), POINTER(c_short)),
132+
(POINTER(c_short * 2), "&(2)<h", None, POINTER(c_short)),
133+
134+
## structures and unions
135+
136+
(Point, "T{<l:x:<l:y:}", None, Point),
137+
# packed structures do not implement the pep
138+
(PackedPoint, "B", None, PackedPoint),
139+
(Point2, "T{<l:x:<l:y:}", None, Point2),
140+
(EmptyStruct, "T{}", None, EmptyStruct),
141+
# the pep does't support unions
142+
(aUnion, "B", None, aUnion),
143+
144+
## other
145+
146+
# function signatures are not implemented
147+
(CFUNCTYPE(None), "X{}", None, CFUNCTYPE(None)),
148+
149+
]
150+
151+
class BEPoint(BigEndianStructure):
152+
_fields_ = [("x", c_long), ("y", c_long)]
153+
154+
class LEPoint(LittleEndianStructure):
155+
_fields_ = [("x", c_long), ("y", c_long)]
156+
157+
################################################################
158+
#
159+
# This table contains format strings as they really look, on both big
160+
# and little endian machines.
161+
#
162+
endian_types = [
163+
(BEPoint, "T{>l:x:>l:y:}", None, BEPoint),
164+
(LEPoint, "T{<l:x:<l:y:}", None, LEPoint),
165+
(POINTER(BEPoint), "&T{>l:x:>l:y:}", None, POINTER(BEPoint)),
166+
(POINTER(LEPoint), "&T{<l:x:<l:y:}", None, POINTER(LEPoint)),
167+
]
168+
169+
if __name__ == "__main__":
170+
unittest.main()

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ Extension Modules
3535
Library
3636
-------
3737

38+
- ctypes objects now support the PEP3118 buffer interface
39+
3840
- Issue #2682: ctypes callback functions now longer contain a cyclic
3941
reference to themselves.
4042

Modules/_ctypes/_ctypes.c

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,36 @@ PyDict_GetItemProxy(PyObject *dict, PyObject *key)
260260
}
261261

262262
/******************************************************************/
263+
/*
264+
Allocate a memory block for a pep3118 format string, copy prefix (if
265+
non-null) and suffix into it. Returns NULL on failure, with the error
266+
indicator set. If called with a suffix of NULL the error indicator must
267+
already be set.
268+
*/
269+
char *
270+
alloc_format_string(const char *prefix, const char *suffix)
271+
{
272+
size_t len;
273+
char *result;
274+
275+
if (suffix == NULL) {
276+
assert(PyErr_Occurred());
277+
return NULL;
278+
}
279+
len = strlen(suffix);
280+
if (prefix)
281+
len += strlen(prefix);
282+
result = PyMem_Malloc(len + 1);
283+
if (result == NULL)
284+
return NULL;
285+
if (prefix)
286+
strcpy(result, prefix);
287+
else
288+
result[0] = '\0';
289+
strcat(result, suffix);
290+
return result;
291+
}
292+
263293
/*
264294
StructType_Type - a meta type/class. Creating a new class using this one as
265295
__metaclass__ will call the contructor StructUnionType_new. It replaces the
@@ -727,6 +757,16 @@ PointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
727757
return NULL;
728758
}
729759

760+
if (proto) {
761+
StgDictObject *itemdict = PyType_stgdict(proto);
762+
assert(itemdict);
763+
stgdict->format = alloc_format_string("&", itemdict->format);
764+
if (stgdict->format == NULL) {
765+
Py_DECREF((PyObject *)stgdict);
766+
return NULL;
767+
}
768+
}
769+
730770
/* create the new instance (which is a class,
731771
since we are a metatype!) */
732772
result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds);
@@ -1101,6 +1141,7 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
11011141
long length;
11021142
int overflow;
11031143
Py_ssize_t itemsize, itemalign;
1144+
char buf[32];
11041145

11051146
typedict = PyTuple_GetItem(args, 2);
11061147
if (!typedict)
@@ -1140,6 +1181,28 @@ ArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
11401181
return NULL;
11411182
}
11421183

1184+
assert(itemdict->format);
1185+
if (itemdict->format[0] == '(') {
1186+
sprintf(buf, "(%ld,", length);
1187+
stgdict->format = alloc_format_string(buf, itemdict->format+1);
1188+
} else {
1189+
sprintf(buf, "(%ld)", length);
1190+
stgdict->format = alloc_format_string(buf, itemdict->format);
1191+
}
1192+
if (stgdict->format == NULL) {
1193+
Py_DECREF((PyObject *)stgdict);
1194+
return NULL;
1195+
}
1196+
stgdict->ndim = itemdict->ndim + 1;
1197+
stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t *) * stgdict->ndim);
1198+
if (stgdict->shape == NULL) {
1199+
Py_DECREF((PyObject *)stgdict);
1200+
return NULL;
1201+
}
1202+
stgdict->shape[0] = length;
1203+
memmove(&stgdict->shape[1], itemdict->shape,
1204+
sizeof(Py_ssize_t) * (stgdict->ndim - 1));
1205+
11431206
itemsize = itemdict->size;
11441207
if (length * itemsize < 0) {
11451208
PyErr_SetString(PyExc_OverflowError,
@@ -1691,6 +1754,16 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
16911754
stgdict->size = fmt->pffi_type->size;
16921755
stgdict->setfunc = fmt->setfunc;
16931756
stgdict->getfunc = fmt->getfunc;
1757+
#ifdef WORDS_BIGENDIAN
1758+
stgdict->format = alloc_format_string(">", proto_str);
1759+
#else
1760+
stgdict->format = alloc_format_string("<", proto_str);
1761+
#endif
1762+
if (stgdict->format == NULL) {
1763+
Py_DECREF(result);
1764+
Py_DECREF((PyObject *)stgdict);
1765+
return NULL;
1766+
}
16941767

16951768
stgdict->paramfunc = SimpleType_paramfunc;
16961769
/*
@@ -1760,22 +1833,32 @@ SimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
17601833
if (type == &SimpleType_Type && fmt->setfunc_swapped && fmt->getfunc_swapped) {
17611834
PyObject *swapped = CreateSwappedType(type, args, kwds,
17621835
proto, fmt);
1836+
StgDictObject *sw_dict;
17631837
if (swapped == NULL) {
17641838
Py_DECREF(result);
17651839
return NULL;
17661840
}
1841+
sw_dict = PyType_stgdict(swapped);
17671842
#ifdef WORDS_BIGENDIAN
17681843
PyObject_SetAttrString((PyObject *)result, "__ctype_le__", swapped);
17691844
PyObject_SetAttrString((PyObject *)result, "__ctype_be__", (PyObject *)result);
17701845
PyObject_SetAttrString(swapped, "__ctype_be__", (PyObject *)result);
17711846
PyObject_SetAttrString(swapped, "__ctype_le__", swapped);
1847+
/* We are creating the type for the OTHER endian */
1848+
sw_dict->format = alloc_format_string("<", stgdict->format+1);
17721849
#else
17731850
PyObject_SetAttrString((PyObject *)result, "__ctype_be__", swapped);
17741851
PyObject_SetAttrString((PyObject *)result, "__ctype_le__", (PyObject *)result);
17751852
PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result);
17761853
PyObject_SetAttrString(swapped, "__ctype_be__", swapped);
1854+
/* We are creating the type for the OTHER endian */
1855+
sw_dict->format = alloc_format_string(">", stgdict->format+1);
17771856
#endif
17781857
Py_DECREF(swapped);
1858+
if (PyErr_Occurred()) {
1859+
Py_DECREF(result);
1860+
return NULL;
1861+
}
17791862
};
17801863

17811864
return (PyObject *)result;
@@ -2025,6 +2108,13 @@ CFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
20252108
return NULL;
20262109

20272110
stgdict->paramfunc = CFuncPtrType_paramfunc;
2111+
/* We do NOT expose the function signature in the format string. It
2112+
is impossible, generally, because the only requirement for the
2113+
argtypes items is that they have a .from_param method - we do not
2114+
know the types of the arguments (although, in practice, most
2115+
argtypes would be a ctypes type).
2116+
*/
2117+
stgdict->format = alloc_format_string(NULL, "X{}");
20282118
stgdict->flags |= TYPEFLAG_ISPOINTER;
20292119

20302120
/* create the new instance (which is a class,
@@ -2240,7 +2330,31 @@ static PyMemberDef CData_members[] = {
22402330
static int CData_GetBuffer(PyObject *_self, Py_buffer *view, int flags)
22412331
{
22422332
CDataObject *self = (CDataObject *)_self;
2243-
return PyBuffer_FillInfo(view, self->b_ptr, self->b_size, 0, flags);
2333+
StgDictObject *dict = PyObject_stgdict(_self);
2334+
Py_ssize_t i;
2335+
2336+
if (view == NULL) return 0;
2337+
if (((flags & PyBUF_LOCK) == PyBUF_LOCK)) {
2338+
PyErr_SetString(PyExc_BufferError,
2339+
"Cannot lock this object.");
2340+
return -1;
2341+
}
2342+
2343+
view->buf = self->b_ptr;
2344+
view->len = self->b_size;
2345+
view->readonly = 0;
2346+
/* use default format character if not set */
2347+
view->format = dict->format ? dict->format : "B";
2348+
view->ndim = dict->ndim;
2349+
view->shape = dict->shape;
2350+
view->itemsize = self->b_size;
2351+
for (i = 0; i < view->ndim; ++i) {
2352+
view->itemsize /= dict->shape[i];
2353+
}
2354+
view->strides = NULL;
2355+
view->suboffsets = NULL;
2356+
view->internal = NULL;
2357+
return 0;
22442358
}
22452359

22462360
static PyBufferProcs CData_as_buffer = {

Modules/_ctypes/callbacks.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
/**************************************************************/
1111

12-
static CThunkObject_dealloc(PyObject *_self)
12+
static void
13+
CThunkObject_dealloc(PyObject *_self)
1314
{
1415
CThunkObject *self = (CThunkObject *)_self;
1516
Py_XDECREF(self->converters);

0 commit comments

Comments
 (0)