diff options
author | Dave Goncalves <davegoncalves@gmail.com> | 2022-03-29 21:26:27 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-29 21:26:27 (GMT) |
commit | dc2d8404a3ab6288ce112c71da8c65c34cd3087e (patch) | |
tree | d34712a90cc6ec0e5080eb9d9c34030424b8a3d2 /Lib/ctypes | |
parent | 654bd2152d82b83d7dc037d6d83d60855e3041b7 (diff) | |
download | cpython-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__.py | 1 | ||||
-rw-r--r-- | Lib/ctypes/_endian.py | 23 | ||||
-rw-r--r-- | Lib/ctypes/test/test_byteswap.py | 115 |
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() |