Skip to content

Adding a ctypes.Union to a ctypes.BigEndianStructure results in an error #87239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
MrSurly mannequin opened this issue Jan 30, 2021 · 3 comments
Closed

Adding a ctypes.Union to a ctypes.BigEndianStructure results in an error #87239

MrSurly mannequin opened this issue Jan 30, 2021 · 3 comments
Labels

Comments

@MrSurly
Copy link
Mannequin

MrSurly mannequin commented Jan 30, 2021

BPO 43073
Nosy @MrSurly

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2021-01-30.04:38:58.603>
labels = ['ctypes', '3.8']
title = 'Adding a ctypes.Union to a ctypes.BigEndianStructure results in an error'
updated_at = <Date 2021-11-07.14:24:54.167>
user = 'https://github.com/MrSurly'

bugs.python.org fields:

activity = <Date 2021-11-07.14:24:54.167>
actor = 'rindeal'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['ctypes']
creation = <Date 2021-01-30.04:38:58.603>
creator = 'MrSurly'
dependencies = []
files = []
hgrepos = []
issue_num = 43073
keywords = []
message_count = 2.0
messages = ['385970', '405906']
nosy_count = 2.0
nosy_names = ['MrSurly', 'rindeal']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue43073'
versions = ['Python 3.8']

@MrSurly
Copy link
Mannequin Author

MrSurly mannequin commented Jan 30, 2021

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 bpo-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)):

@MrSurly MrSurly mannequin added 3.8 (EOL) end of life topic-ctypes labels Jan 30, 2021
@rindeal
Copy link
Mannequin

rindeal mannequin commented Nov 7, 2021

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"

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@encukou
Copy link
Member

encukou commented May 13, 2025

This works in Python 3.11; presumably due to #25480.

@encukou encukou closed this as completed May 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant