classification
Title: Adding a ctypes.Union to a ctypes.BigEndianStructure results in an error
Type: Stage:
Components: ctypes Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: MrSurly, rindeal
Priority: normal Keywords:

Created on 2021-01-30 04:38 by MrSurly, last changed 2021-11-07 14:24 by rindeal.

Messages (2)
msg385970 - (view) Author: Eric Poulsen (MrSurly) Date: 2021-01-30 04:38
Placing a ctypes.Union inside of a ctypes.BigEndianStructure results in "TypeError: This type does not support other endian".  I believe this is a similar problem to issue #4376 (https://bugs.python.org/issue4376)

Minimum repro test case:

import ctypes as ct

class U(ct.Union):
    _pack_=True
    _fields_=[
        ('a', ct.c_int),
        ('b', ct.c_int),
    ]

class S(ct.BigEndianStructure):
    _pack_=True
    _fields_=[
        ('x', ct.c_int),
        ('y', U),
    ]

I believe the fix is similar to that issue, though I admit I don't know enough about this code to be sure.

diff --git a/Lib/ctypes/_endian.py b/Lib/ctypes/_endian.py
index 37444bd6a7..525c5e58c9 100644
--- a/Lib/ctypes/_endian.py
+++ b/Lib/ctypes/_endian.py
@@ -18,6 +18,9 @@ def _other_endian(typ):
     # if typ is structure
     if issubclass(typ, Structure):
         return typ
+    # if typ is union:
+    if issubclass(typ, Union):
+        return typ
     raise TypeError("This type does not support other endian: %s" % typ)
 
 class _swapped_meta(type(Structure)):
msg405906 - (view) Author: Jan Chren (rindeal) Date: 2021-11-07 14:24
I have created a workaround, since it might take years to fix this in master. Hope it'll come in useful.


For the example in https://bugs.python.org/issue43073#msg385970, but probably any combination of Unions and BigEndianStructures can be constructed this way.
```
class U_a(ct.BigEndianStructure):
    _pack_ = True
    _fields_ = [('a', ct.c_int)]

class U_b(ct.BigEndianStructure):
    _pack_ = True
    _fields_ = [('b', ct.c_int)]

class U(ct.Union):
    _pack_ = True
    _fields_ = [
        ('_a', U_a),
        ('_b', U_b),
    ]
    _anonymous_ = ['_a', '_b']

class _S_be_fields_only(ct.Structure):
    _pack_ = True
    _fields_ = [
        ('_x', ct.c_int),
        ('y', U),
    ]
class _S_2be_fields_only(ct.BigEndianStructure):
    _pack_ = True
    _fields_ = [
        ('x', ct.c_int),
        ('_y', ct.c_byte * ct.sizeof(U)),
    ]

class _S_U(ct.Union):
    _pack_ = True
    _fields_ = [
        ('_be_fields_only', _S_be_fields_only),
        ('_2be_fields_only', _S_2be_fields_only),
    ]
    _anonymous_ = [f[0] for f in _fields_]

class S(ct.Structure):
    _pack_ = True
    _fields_ = [('_s_u', _S_U)]
    _anonymous_ = [_fields_[0][0]]


issubclass(S, ct.Structure) == True
s = S(x=0x11223344, y=U(a=0xaabbccdd))
s.y.a == s.y.b
bytes(s).hex() == "11223344aabbccdd"
```
History
Date User Action Args
2021-11-07 14:24:54rindealsetnosy: + rindeal
messages: + msg405906
2021-01-30 04:38:58MrSurlycreate