8000 bpo-33178: Add BigEndianUnion, LittleEndianUnion classes to ctypes (G… · python/cpython@dc2d840 · GitHub
[go: up one dir, main page]

Skip to content

Commit dc2d840

Browse files
authored
bpo-33178: Add BigEndianUnion, LittleEndianUnion classes to ctypes (GH-25480)
* bpo-33178: Add BigEndianUnion, LittleEndianUnion classes to ctypes * GH-25480: remove trailing whitespace in ctypes doc * GH-25480: add news entry blurb * GH-25480: corrected formatting error in news blurb * GH-25480: simplified, corrected formatting in news blurb * GH-25480: remove trailing whitespace in news blurb * GH-25480: fixed class markup in news blurb * GH-25480: fixed unsupported type tests and naming per review comments * GH-25480: fixed whitepace errors * condensed base class selection for unsupported byte order tests * added versionadded tags for new EndianUnion classes
1 parent 654bd21 commit dc2d840

File tree

5 files changed

+118
-38
lines changed

5 files changed

+118
-38
lines changed

Doc/library/ctypes.rst

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2390,6 +2390,18 @@ Structured data types
23902390
Abstract base class for unions in native byte order.
23912391

23922392

2393+
.. class:: BigEndianUnion(*args, **kw)
2394+
2395+
Abstract base class for unions in *big endian* byte order.
2396+
2397+
.. versionadded:: 3.11
2398+
2399+
.. class:: LittleEndianUnion(*args, **kw)
2400+
2401+
Abstract base class for unions in *little endian* byte order.
2402+
2403+
.. versionadded:: 3.11
2404+
23932405
.. class:: BigEndianStructure(*args, **kw)
23942406

23952407
Abstract base class for structures in *big endian* byte order.
@@ -2399,8 +2411,8 @@ Structured data types
23992411

24002412
Abstract base class for structures in *little endian* byte order.
24012413

2402-
Structures with non-native byte order cannot contain pointer type fields, or any
2403-
other data types containing pointer type fields.
2414+
Structures and unions with non-native byte order cannot contain pointer type
2415+
fields, or any other data types containing pointer type fields.
24042416

24052417

24062418
.. class:: Structure(*args, **kw)

Lib/ctypes/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ def DllCanUnloadNow():
548548
return ccom.DllCanUnloadNow()
549549

550550
from ctypes._endian import BigEndianStructure, LittleEndianStructure
551+
from ctypes._endian import BigEndianUnion, LittleEndianUnion
551552

552553
# Fill in specifically-sized types
553554
c_int8 = c_byte

Lib/ctypes/_endian.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def _other_endian(typ):
2020
return typ
2121
raise TypeError("This type does not support other endian: %s" % typ)
2222

23-
class _swapped_meta(type(Structure)):
23+
class _swapped_meta:
2424
def __setattr__(self, attrname, value):
2525
if attrname == "_fields_":
2626
fields = []
@@ -31,6 +31,8 @@ def __setattr__(self, attrname, value):
3131
fields.append((name, _other_endian(typ)) + rest)
3232
value = fields
3333
super().__setattr__(attrname, value)
34+
class _swapped_struct_meta(_swapped_meta, type(Structure)): pass
35+
class _swapped_union_meta(_swapped_meta, type(Union)): pass
3436

3537
################################################################
3638

@@ -43,19 +45,34 @@ def __setattr__(self, attrname, value):
4345

4446
LittleEndianStructure = Structure
4547

46-
class BigEndianStructure(Structure, metaclass=_swapped_meta):
48+
class BigEndianStructure(Structure, metaclass=_swapped_struct_meta):
4749
"""Structure with big endian byte order"""
4850
__slots__ = ()
4951
_swappedbytes_ = None
5052

53+
LittleEndianUnion = Union
54+
55+
class BigEndianUnion(Union, metaclass=_swapped_union_meta):
56+
"""Union with big endian byte order"""
57+
__slots__ = ()
58+
_swappedbytes_ = None
59+
5160
elif sys.byteorder == "big":
5261
_OTHER_ENDIAN = "__ctype_le__"
5362

5463
BigEndianStructure = Structure
55-
class LittleEndianStructure(Structure, metaclass=_swapped_meta):
64+
65+
class LittleEndianStructure(Structure, metaclass=_swapped_struct_meta):
5666
"""Structure with little endian byte order"""
5767
__slots__ = ()
5868
_swappedbytes_ = None
5969

70+
BigEndianUnion = Union
71+
72+
class LittleEndianUnion(Union, metaclass=_swapped_union_meta):
73+
"""Union with little endian byte order"""
74+
__slots__ = ()
75+
_swappedbytes_ = None
76+
6077
else:
6178
raise RuntimeError("Invalid byteorder")

Lib/ctypes/test/test_byteswap.py

Lines changed: 82 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -170,40 +170,34 @@ def test_endian_other(self):
170170
self.assertIs(c_char.__ctype_le__, c_char)
171171
self.assertIs(c_char.__ctype_be__, c_char)
172172

173-
def test_struct_fields_1(self):
174-
if sys.byteorder == "little":
175-
base = BigEndianStructure
176-
else:
177-
base = LittleEndianStructure
178-
179-
class T(base):
180-
pass
181-
_fields_ = [("a", c_ubyte),
182-
("b", c_byte),
183-
("c", c_short),
184-
("d", c_ushort),
185-
("e", c_int),
186-
("f", c_uint),
187-
("g", c_long),
188-
("h", c_ulong),
189-
("i", c_longlong),
190-
("k", c_ulonglong),
191-
("l", c_float),
192-
("m", c_double),
193-
("n", c_char),
194-
195-
("b1", c_byte, 3),
196-
("b2", c_byte, 3),
197-
("b3", c_byte, 2),
198-
("a", c_int * 3 * 3 * 3)]
199-
T._fields_ = _fields_
173+
def test_struct_fields_unsupported_byte_order(self):
174+
175+
fields = [
176+
("a", c_ubyte),
177+
("b", c_byte),
178+
("c", c_short),
179+
("d", c_ushort),
180+
("e", c_int),
181+
("f", c_uint),
182+
("g", c_long),
183+
("h", c_ulong),
184+
("i", c_longlong),
185+
("k", c_ulonglong),
186+
("l", c_float),
187+
("m", c_double),
188+
("n", c_char),
189+
("b1", c_byte, 3),
190+
("b2", c_byte, 3),
191+
("b3", c_byte, 2),
192+
("a", c_int * 3 * 3 * 3)
193+
]
200194

201195
# these fields do not support different byte order:
202196
for typ in c_wchar, c_void_p, POINTER(c_int):
203-
_fields_.append(("x", typ))
204-
class T(base):
205-
pass
206-
self.assertRaises(TypeError, setattr, T, "_fields_", [("x", typ)])
197+
with self.assertRaises(TypeError):
198+
class T(BigEndianStructure if sys.byteorder == "little" else LittleEndianStructure):
199+
_fields_ = fields + [("x", typ)]
200+
207201

208202
def test_struct_struct(self):
209203
# nested structures with different byteorders
@@ -233,7 +227,7 @@ class TestStructure(parent):
233227
self.assertEqual(s.point.x, 1)
234228
self.assertEqual(s.point.y, 2)
235229

236-
def test_struct_fields_2(self):
230+
def test_struct_field_alignment(self):
237231
# standard packing in struct uses no alignment.
238232
# So, we have to align using pad bytes.
239233
#
@@ -267,7 +261,6 @@ def test_unaligned_nonnative_struct_fields(self):
267261
class S(base):
268262
_pack_ = 1
269263
_fields_ = [("b", c_byte),
270-
271264
("h", c_short),
272265

273266
("_1", c_byte),
@@ -311,5 +304,61 @@ class S(Structure):
311304
s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
312305
self.assertEqual(bin(s1), bin(s2))
313306

307+
def test_union_fields_unsupported_byte_order(self):
308+
309+
fields = [
310+
("a", c_ubyte),
311+
("b", c_byte),
312+
("c", c_short),
313+
("d", c_ushort),
314+
("e", c_int),
315+
("f", c_uint),
316+
("g", c_long),
317+
("h", c_ulong),
318+
("i", c_longlong),
319+
("k", c_ulonglong),
320+
("l", c_float),
321+
("m", c_double),
322+
("n", c_char),
323+
("b1", c_byte, 3),
324+
("b2", c_byte, 3),
325+
("b3", c_byte, 2),
326+
("a", c_int * 3 * 3 * 3)
327+
]
328+
329+
# these fields do not support different byte order:
330+
for typ in c_wchar, c_void_p, POINTER(c_int):
331+
with self.assertRaises(TypeError):
332+
class T(BigEndianUnion if sys.byteorder == "little" else LittleEndianUnion):
333+
_fields_ = fields + [("x", typ)]
334+
335+
def test_union_struct(self):
336+
# nested structures in unions with different byteorders
337+
338+
# create nested structures in unions with given byteorders and set memory to data
339+
340+
for nested, data in (
341+
(BigEndianStructure, b'\0\0\0\1\0\0\0\2'),
342+
(LittleEndianStructure, b'\1\0\0\0\2\0\0\0'),
343+
):
344+
for parent in (
345+
BigEndianUnion,
346+
LittleEndianUnion,
347+
Union,
348+
):
349+
class NestedStructure(nested):
350+
_fields_ = [("x", c_uint32),
351+
("y", c_uint32)]
352+
353+
class TestUnion(parent):
354+
_fields_ = [("point", NestedStructure)]
355+
356+
self.assertEqual(len(data), sizeof(TestUnion))
357+
ptr = POINTER(TestUnion)
358+
s = cast(data, ptr)[0]
359+
del ctypes._pointer_type_cache[TestUnion]
360+
self.assertEqual(s.point.x, 1)
361+
self.assertEqual(s.point.y, 2)
362+
314363
if __name__ == "__main__":
315364
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added :class:`ctypes.BigEndianUnion` and :class:`ctypes.LittleEndianUnion` classes, as originally documented in the library docs but not yet implemented.

0 commit comments

Comments
 (0)
0