mirror of https://github.com/python/cpython
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
This commit is contained in:
parent
654bd2152d
commit
dc2d8404a3
|
@ -2390,6 +2390,18 @@ Structured data types
|
|||
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.
|
||||
|
@ -2399,8 +2411,8 @@ Structured data types
|
|||
|
||||
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)
|
||||
|
|
|
@ -548,6 +548,7 @@ if _os.name == "nt": # COM stuff
|
|||
return ccom.DllCanUnloadNow()
|
||||
|
||||
from ctypes._endian import BigEndianStructure, LittleEndianStructure
|
||||
from ctypes._endian import BigEndianUnion, LittleEndianUnion
|
||||
|
||||
# Fill in specifically-sized types
|
||||
c_int8 = c_byte
|
||||
|
|
|
@ -20,7 +20,7 @@ def _other_endian(typ):
|
|||
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 = []
|
||||
|
@ -31,6 +31,8 @@ class _swapped_meta(type(Structure)):
|
|||
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
|
||||
|
||||
################################################################
|
||||
|
||||
|
@ -43,19 +45,34 @@ if sys.byteorder == "little":
|
|||
|
||||
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")
|
||||
|
|
|
@ -170,40 +170,34 @@ class Test(unittest.TestCase):
|
|||
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
|
||||
def test_struct_fields_unsupported_byte_order(self):
|
||||
|
||||
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_
|
||||
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
|
||||
|
@ -233,7 +227,7 @@ class Test(unittest.TestCase):
|
|||
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.
|
||||
#
|
||||
|
@ -267,7 +261,6 @@ class Test(unittest.TestCase):
|
|||
class S(base):
|
||||
_pack_ = 1
|
||||
_fields_ = [("b", c_byte),
|
||||
|
||||
("h", c_short),
|
||||
|
||||
("_1", c_byte),
|
||||
|
@ -311,5 +304,61 @@ class Test(unittest.TestCase):
|
|||
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()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Added :class:`ctypes.BigEndianUnion` and :class:`ctypes.LittleEndianUnion` classes, as originally documented in the library docs but not yet implemented.
|
Loading…
Reference in New Issue