Abstract base class for unions in native byte order.
+.. class:: BigEndianUnion(*args, **kw)
+
+ Abstract base class for unions in *big endian* byte order.
+
+ .. versionadded:: 3.11
+
+.. class:: LittleEndianUnion(*args, **kw)
+
+ Abstract base class for unions in *little endian* byte order.
+
+ .. versionadded:: 3.11
+
.. class:: BigEndianStructure(*args, **kw)
Abstract base class for structures in *big endian* byte order.
Abstract base class for structures in *little endian* byte order.
-Structures with non-native byte order cannot contain pointer type fields, or any
-other data types containing pointer type fields.
+Structures and unions with non-native byte order cannot contain pointer type
+fields, or any other data types containing pointer type fields.
.. class:: Structure(*args, **kw)
return typ
raise TypeError("This type does not support other endian: %s" % typ)
-class _swapped_meta(type(Structure)):
+class _swapped_meta:
def __setattr__(self, attrname, value):
if attrname == "_fields_":
fields = []
fields.append((name, _other_endian(typ)) + rest)
value = fields
super().__setattr__(attrname, value)
+class _swapped_struct_meta(_swapped_meta, type(Structure)): pass
+class _swapped_union_meta(_swapped_meta, type(Union)): pass
################################################################
LittleEndianStructure = Structure
- class BigEndianStructure(Structure, metaclass=_swapped_meta):
+ class BigEndianStructure(Structure, metaclass=_swapped_struct_meta):
"""Structure with big endian byte order"""
__slots__ = ()
_swappedbytes_ = None
+ LittleEndianUnion = Union
+
+ class BigEndianUnion(Union, metaclass=_swapped_union_meta):
+ """Union with big endian byte order"""
+ __slots__ = ()
+ _swappedbytes_ = None
+
elif sys.byteorder == "big":
_OTHER_ENDIAN = "__ctype_le__"
BigEndianStructure = Structure
- class LittleEndianStructure(Structure, metaclass=_swapped_meta):
+
+ class LittleEndianStructure(Structure, metaclass=_swapped_struct_meta):
"""Structure with little endian byte order"""
__slots__ = ()
_swappedbytes_ = None
+ BigEndianUnion = Union
+
+ class LittleEndianUnion(Union, metaclass=_swapped_union_meta):
+ """Union with little endian byte order"""
+ __slots__ = ()
+ _swappedbytes_ = None
+
else:
raise RuntimeError("Invalid byteorder")
self.assertIs(c_char.__ctype_le__, c_char)
self.assertIs(c_char.__ctype_be__, c_char)
- def test_struct_fields_1(self):
- if sys.byteorder == "little":
- base = BigEndianStructure
- else:
- base = LittleEndianStructure
-
- class T(base):
- pass
- _fields_ = [("a", c_ubyte),
- ("b", c_byte),
- ("c", c_short),
- ("d", c_ushort),
- ("e", c_int),
- ("f", c_uint),
- ("g", c_long),
- ("h", c_ulong),
- ("i", c_longlong),
- ("k", c_ulonglong),
- ("l", c_float),
- ("m", c_double),
- ("n", c_char),
-
- ("b1", c_byte, 3),
- ("b2", c_byte, 3),
- ("b3", c_byte, 2),
- ("a", c_int * 3 * 3 * 3)]
- T._fields_ = _fields_
+ def test_struct_fields_unsupported_byte_order(self):
+
+ fields = [
+ ("a", c_ubyte),
+ ("b", c_byte),
+ ("c", c_short),
+ ("d", c_ushort),
+ ("e", c_int),
+ ("f", c_uint),
+ ("g", c_long),
+ ("h", c_ulong),
+ ("i", c_longlong),
+ ("k", c_ulonglong),
+ ("l", c_float),
+ ("m", c_double),
+ ("n", c_char),
+ ("b1", c_byte, 3),
+ ("b2", c_byte, 3),
+ ("b3", c_byte, 2),
+ ("a", c_int * 3 * 3 * 3)
+ ]
# these fields do not support different byte order:
for typ in c_wchar, c_void_p, POINTER(c_int):
- _fields_.append(("x", typ))
- class T(base):
- pass
- self.assertRaises(TypeError, setattr, T, "_fields_", [("x", typ)])
+ with self.assertRaises(TypeError):
+ class T(BigEndianStructure if sys.byteorder == "little" else LittleEndianStructure):
+ _fields_ = fields + [("x", typ)]
+
def test_struct_struct(self):
# nested structures with different byteorders
self.assertEqual(s.point.x, 1)
self.assertEqual(s.point.y, 2)
- def test_struct_fields_2(self):
+ def test_struct_field_alignment(self):
# standard packing in struct uses no alignment.
# So, we have to align using pad bytes.
#
class S(base):
_pack_ = 1
_fields_ = [("b", c_byte),
-
("h", c_short),
("_1", c_byte),
s2 = struct.pack(fmt, 0x12, 0x1234, 0x12345678, 3.14)
self.assertEqual(bin(s1), bin(s2))
+ def test_union_fields_unsupported_byte_order(self):
+
+ fields = [
+ ("a", c_ubyte),
+ ("b", c_byte),
+ ("c", c_short),
+ ("d", c_ushort),
+ ("e", c_int),
+ ("f", c_uint),
+ ("g", c_long),
+ ("h", c_ulong),
+ ("i", c_longlong),
+ ("k", c_ulonglong),
+ ("l", c_float),
+ ("m", c_double),
+ ("n", c_char),
+ ("b1", c_byte, 3),
+ ("b2", c_byte, 3),
+ ("b3", c_byte, 2),
+ ("a", c_int * 3 * 3 * 3)
+ ]
+
+ # these fields do not support different byte order:
+ for typ in c_wchar, c_void_p, POINTER(c_int):
+ with self.assertRaises(TypeError):
+ class T(BigEndianUnion if sys.byteorder == "little" else LittleEndianUnion):
+ _fields_ = fields + [("x", typ)]
+
+ def test_union_struct(self):
+ # nested structures in unions with different byteorders
+
+ # create nested structures in unions with given byteorders and set memory to data
+
+ for nested, data in (
+ (BigEndianStructure, b'\0\0\0\1\0\0\0\2'),
+ (LittleEndianStructure, b'\1\0\0\0\2\0\0\0'),
+ ):
+ for parent in (
+ BigEndianUnion,
+ LittleEndianUnion,
+ Union,
+ ):
+ class NestedStructure(nested):
+ _fields_ = [("x", c_uint32),
+ ("y", c_uint32)]
+
+ class TestUnion(parent):
+ _fields_ = [("point", NestedStructure)]
+
+ self.assertEqual(len(data), sizeof(TestUnion))
+ ptr = POINTER(TestUnion)
+ s = cast(data, ptr)[0]
+ del ctypes._pointer_type_cache[TestUnion]
+ self.assertEqual(s.point.x, 1)
+ self.assertEqual(s.point.y, 2)
+
if __name__ == "__main__":
unittest.main()