Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2d1653a

Browse filesBrowse files
pvpitrou
authored andcommitted
[3.6] bpo-10746: Fix ctypes PEP 3118 type codes for c_long, c_bool, c_int (GH-31) (#3241)
Ctypes currently produces wrong pep3118 type codes for several types. E.g. memoryview(ctypes.c_long()).format gives "<l" on 64-bit platforms, but it should be "<q" instead for sizeof(c_long) == 8 The problem is that the '<>' endian specification in the struct syntax also turns on the "standard size" mode, which makes type characters have a platform-independent meaning, which does not match with the codes used internally in ctypes. The struct module format syntax also does not allow specifying native-size non-native-endian items. This commit adds a converter function that maps the internal ctypes codes to appropriate struct module standard-size codes in the pep3118 format strings. The tests are modified to check for this. (cherry picked from commit 07f1658)
1 parent 87c5024 commit 2d1653a
Copy full SHA for 2d1653a

File tree

3 files changed

+123
-30
lines changed
Filter options

3 files changed

+123
-30
lines changed

‎Lib/ctypes/test/test_pep3118.py

Copy file name to clipboardExpand all lines: Lib/ctypes/test/test_pep3118.py
+55-28Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,34 @@ class Complete(Structure):
112112
# This table contains format strings as they look on little endian
113113
# machines. The test replaces '<' with '>' on big endian machines.
114114
#
115+
116+
# Platform-specific type codes
117+
s_bool = {1: '?', 2: 'H', 4: 'L', 8: 'Q'}[sizeof(c_bool)]
118+
s_short = {2: 'h', 4: 'l', 8: 'q'}[sizeof(c_short)]
119+
s_ushort = {2: 'H', 4: 'L', 8: 'Q'}[sizeof(c_ushort)]
120+
s_int = {2: 'h', 4: 'i', 8: 'q'}[sizeof(c_int)]
121+
s_uint = {2: 'H', 4: 'I', 8: 'Q'}[sizeof(c_uint)]
122+
s_long = {4: 'l', 8: 'q'}[sizeof(c_long)]
123+
s_ulong = {4: 'L', 8: 'Q'}[sizeof(c_ulong)]
124+
s_longlong = "q"
125+
s_ulonglong = "Q"
126+
s_float = "f"
127+
s_double = "d"
128+
s_longdouble = "g"
129+
130+
# Alias definitions in ctypes/__init__.py
131+
if c_int is c_long:
132+
s_int = s_long
133+
if c_uint is c_ulong:
134+
s_uint = s_ulong
135+
if c_longlong is c_long:
136+
s_longlong = s_long
137+
if c_ulonglong is c_ulong:
138+
s_ulonglong = s_ulong
139+
if c_longdouble is c_double:
140+
s_longdouble = s_double
141+
142+
115143
native_types = [
116144
# type format shape calc itemsize
117145

@@ -120,60 +148,59 @@ class Complete(Structure):
120148
(c_char, "<c", (), c_char),
121149
(c_byte, "<b", (), c_byte),
122150
(c_ubyte, "<B", (), c_ubyte),
123-
(c_short, "<h", (), c_short),
124-
(c_ushort, "<H", (), c_ushort),
151+
(c_short, "<" + s_short, (), c_short),
152+
(c_ushort, "<" + s_ushort, (), c_ushort),
125153

126-
# c_int and c_uint may be aliases to c_long
127-
#(c_int, "<i", (), c_int),
128-
#(c_uint, "<I", (), c_uint),
154+
(c_int, "<" + s_int, (), c_int),
155+
(c_uint, "<" + s_uint, (), c_uint),
129156

130-
(c_long, "<l", (), c_long),
131-
(c_ulong, "<L", (), c_ulong),
157+
(c_long, "<" + s_long, (), c_long),
158+
(c_ulong, "<" + s_ulong, (), c_ulong),
132159

133-
# c_longlong and c_ulonglong are aliases on 64-bit platforms
134-
#(c_longlong, "<q", None, c_longlong),
135-
#(c_ulonglong, "<Q", None, c_ulonglong),
160+
(c_longlong, "<" + s_longlong, (), c_longlong),
161+
(c_ulonglong, "<" + s_ulonglong, (), c_ulonglong),
136162

137163
(c_float, "<f", (), c_float),
138164
(c_double, "<d", (), c_double),
139-
# c_longdouble may be an alias to c_double
140165

141-
(c_bool, "<?", (), c_bool),
166+
(c_longdouble, "<" + s_longdouble, (), c_longdouble),
167+
168+
(c_bool, "<" + s_bool, (), c_bool),
142169
(py_object, "<O", (), py_object),
143170

144171
## pointers
145172

146173
(POINTER(c_byte), "&<b", (), POINTER(c_byte)),
147-
(POINTER(POINTER(c_long)), "&&<l", (), POINTER(POINTER(c_long))),
174+
(POINTER(POINTER(c_long)), "&&<" + s_long, (), POINTER(POINTER(c_long))),
148175

149176
## arrays and pointers
150177

151178
(c_double * 4, "<d", (4,), c_double),
152179
(c_float * 4 * 3 * 2, "<f", (2,3,4), c_float),
153-
(POINTER(c_short) * 2, "&<h", (2,), POINTER(c_short)),
154-
(POINTER(c_short) * 2 * 3, "&<h", (3,2,), POINTER(c_short)),
155-
(POINTER(c_short * 2), "&(2)<h", (), POINTER(c_short)),
180+
(POINTER(c_short) * 2, "&<" + s_short, (2,), POINTER(c_short)),
181+
(POINTER(c_short) * 2 * 3, "&<" + s_short, (3,2,), POINTER(c_short)),
182+
(POINTER(c_short * 2), "&(2)<" + s_short, (), POINTER(c_short)),
156183

157184
## structures and unions
158185

159-
(Point, "T{<l:x:<l:y:}", (), Point),
186+
(Point, "T{<l:x:<l:y:}".replace('l', s_long), (), Point),
160187
# packed structures do not implement the pep
161-
(PackedPoint, "B", (), PackedPoint),
162-
(Point2, "T{<l:x:<l:y:}", (), Point2),
163-
(EmptyStruct, "T{}", (), EmptyStruct),
188+
(PackedPoint, "B", (), PackedPoint),
189+
(Point2, "T{<l:x:<l:y:}".replace('l', s_long), (), Point2),
190+
(EmptyStruct, "T{}", (), EmptyStruct),
164191
# the pep does't support unions
165-
(aUnion, "B", (), aUnion),
192+
(aUnion, "B", (), aUnion),
166193
# structure with sub-arrays
167-
(StructWithArrays, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (), StructWithArrays),
168-
(StructWithArrays * 3, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}", (3,), StructWithArrays),
194+
(StructWithArrays, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}".replace('l', s_long), (), StructWithArrays),
195+
(StructWithArrays * 3, "T{(2,3)<l:x:(4)T{<l:x:<l:y:}:y:}".replace('l', s_long), (3,), StructWithArrays),
169196

170197
## pointer to incomplete structure
171198
(Incomplete, "B", (), Incomplete),
172199
(POINTER(Incomplete), "&B", (), POINTER(Incomplete)),
173200

174201
# 'Complete' is a structure that starts incomplete, but is completed after the
175202
# pointer type to it has been created.
176-
(Complete, "T{<l:a:}", (), Complete),
203+
(Complete, "T{<l:a:}".replace('l', s_long), (), Complete),
177204
# Unfortunately the pointer format string is not fixed...
178205
(POINTER(Complete), "&B", (), POINTER(Complete)),
179206

@@ -196,10 +223,10 @@ class LEPoint(LittleEndianStructure):
196223
# and little endian machines.
197224
#
198225
endian_types = [
199-
(BEPoint, "T{>l:x:>l:y:}", (), BEPoint),
200-
(LEPoint, "T{<l:x:<l:y:}", (), LEPoint),
201-
(POINTER(BEPoint), "&T{>l:x:>l:y:}", (), POINTER(BEPoint)),
202-
(POINTER(LEPoint), "&T{<l:x:<l:y:}", (), POINTER(LEPoint)),
226+
(BEPoint, "T{>l:x:>l:y:}".replace('l', s_long), (), BEPoint),
227+
(LEPoint, "T{<l:x:<l:y:}".replace('l', s_long), (), LEPoint),
228+
(POINTER(BEPoint), "&T{>l:x:>l:y:}".replace('l', s_long), (), POINTER(BEPoint)),
229+
(POINTER(LEPoint), "&T{<l:x:<l:y:}".replace('l', s_long), (), POINTER(LEPoint)),
203230
]
204231

205232
if __name__ == "__main__":
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ctypes producing wrong PEP 3118 type codes for integer types.

‎Modules/_ctypes/_ctypes.c

Copy file name to clipboardExpand all lines: Modules/_ctypes/_ctypes.c
+67-2Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,71 @@ PyDict_GetItemProxy(PyObject *dict, PyObject *key)
250250
}
251251

252252
/******************************************************************/
253+
254+
/*
255+
Allocate a memory block for a pep3118 format string, filled with
256+
a suitable PEP 3118 type code corresponding to the given ctypes
257+
type. Returns NULL on failure, with the error indicator set.
258+
259+
This produces type codes in the standard size mode (cf. struct module),
260+
since the endianness may need to be swapped to a non-native one
261+
later on.
262+
*/
263+
static char *
264+
_ctypes_alloc_format_string_for_type(char code, int big_endian)
265+
{
266+
char *result;
267+
char pep_code = '\0';
268+
269+
switch (code) {
270+
#if SIZEOF_INT == 2
271+
case 'i': pep_code = 'h'; break;
272+
case 'I': pep_code = 'H'; break;
273+
#elif SIZEOF_INT == 4
274+
case 'i': pep_code = 'i'; break;
275+
case 'I': pep_code = 'I'; break;
276+
#elif SIZEOF_INT == 8
277+
case 'i': pep_code = 'q'; break;
278+
case 'I': pep_code = 'Q'; break;
279+
#else
280+
# error SIZEOF_INT has an unexpected value
281+
#endif /* SIZEOF_INT */
282+
#if SIZEOF_LONG == 4
283+
case 'l': pep_code = 'l'; break;
284+
case 'L': pep_code = 'L'; break;
285+
#elif SIZEOF_LONG == 8
286+
case 'l': pep_code = 'q'; break;
287+
case 'L': pep_code = 'Q'; break;
288+
#else
289+
# error SIZEOF_LONG has an unexpected value
290+
#endif /* SIZEOF_LONG */
291+
#if SIZEOF__BOOL == 1
292+
case '?': pep_code = '?'; break;
293+
#elif SIZEOF__BOOL == 2
294+
case '?': pep_code = 'H'; break;
295+
#elif SIZEOF__BOOL == 4
296+
case '?': pep_code = 'L'; break;
297+
#elif SIZEOF__BOOL == 8
298+
case '?': pep_code = 'Q'; break;
299+
#else
300+
# error SIZEOF__BOOL has an unexpected value
301+
#endif /* SIZEOF__BOOL */
302+
default:
303+
/* The standard-size code is the same as the ctypes one */
304+
pep_code = code;
305+
break;
306+
}
307+
308+
result = PyMem_Malloc(3);
309+
if (result == NULL)
310+
return NULL;
311+
312+
result[0] = big_endian ? '>' : '<';
313+
result[1] = pep_code;
314+
result[2] = '\0';
315+
return result;
316+
}
317+
253318
/*
254319
Allocate a memory block for a pep3118 format string, copy prefix (if
255320
non-null) and suffix into it. Returns NULL on failure, with the error
@@ -1926,9 +1991,9 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
19261991
stgdict->setfunc = fmt->setfunc;
19271992
stgdict->getfunc = fmt->getfunc;
19281993
#ifdef WORDS_BIGENDIAN
1929-
stgdict->format = _ctypes_alloc_format_string(">", proto_str);
1994+
stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1);
19301995
#else
1931-
stgdict->format = _ctypes_alloc_format_string("<", proto_str);
1996+
stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0);
19321997
#endif
19331998
if (stgdict->format == NULL) {
19341999
Py_DECREF(result);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.