summaryrefslogtreecommitdiffstats
path: root/Lib/ctypes
diff options
context:
space:
mode:
authorDave Goncalves <davegoncalves@gmail.com>2022-03-29 21:26:27 (GMT)
committerGitHub <noreply@github.com>2022-03-29 21:26:27 (GMT)
commitdc2d8404a3ab6288ce112c71da8c65c34cd3087e (patch)
treed34712a90cc6ec0e5080eb9d9c34030424b8a3d2 /Lib/ctypes
parent654bd2152d82b83d7dc037d6d83d60855e3041b7 (diff)
downloadcpython-dc2d8404a3ab6288ce112c71da8c65c34cd3087e.zip
cpython-dc2d8404a3ab6288ce112c71da8c65c34cd3087e.tar.gz
cpython-dc2d8404a3ab6288ce112c71da8c65c34cd3087e.tar.bz2
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
Diffstat (limited to 'Lib/ctypes')
-rw-r--r--Lib/ctypes/__init__.py1
-rw-r--r--Lib/ctypes/_endian.py23
-rw-r--r--Lib/ctypes/test/test_byteswap.py115
3 files changed, 103 insertions, 36 deletions
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index ab4d31b..26135ad 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -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
diff --git a/Lib/ctypes/_endian.py b/Lib/ctypes/_endian.py
index 37444bd..34dee64 100644
--- a/Lib/ctypes/_endian.py
+++ b/Lib/ctypes/_endian.py
@@ -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")
diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py
index 01c97e8..7e98559 100644
--- a/Lib/ctypes/test/test_byteswap.py
+++ b/Lib/ctypes/test/test_byteswap.py
@@ -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
-
- 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
@@ -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()