Last active
July 4, 2024 21:46
-
-
Save christoph2/9c390e5c094796903097 to your computer and use it in GitHub Desktop.
Add missing enum feature to ctypes Structures.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes | |
import enum | |
# | |
# Prerequisits: | |
# ------------- | |
# If you are using Python < 3.4 run `pip install enum34`. | |
# | |
# Problem Definition | |
# ------------------ | |
# ctypes.Structure class doesn't support enumerations out-of-the-box. | |
# Consider the following (non-working) example: | |
# | |
# class EnableDisableType(enum.IntEnum): | |
# ENABLE = 1 | |
# DISABLE = 2 | |
# class SomeStructure(ctypes.Structure): | |
# _fields_ = [ | |
# ("value", ctypes.c_uint32), | |
# ("switch", EnableDisableType), | |
# ] | |
# | |
# Solution: | |
# --------- | |
# 1). Use the following `StructureWithEnums` class instead of `ctypes.Structure`. | |
# 2). Add `_map`attribute to your structure definition (which maps field names to enumeration types). | |
# 3) Replace enumeration types in your `_fields_` list with `ctypes.c_int`. | |
# Voilà! | |
# | |
# class SomeStructure(StructureWithEnums): | |
# _fields_ = [ | |
# ("value", ctypes.c_uint32), | |
# ("switch", ctypes.c_int), | |
# ] | |
# _map = { | |
# "switch": EnableDisableType | |
# } | |
# | |
class StructureWithEnums(ctypes.Structure): | |
"""Add missing enum feature to ctypes Structures. | |
""" | |
_map = {} | |
def __getattribute__(self, name): | |
_map = ctypes.Structure.__getattribute__(self, '_map') | |
value = ctypes.Structure.__getattribute__(self, name) | |
if name in _map: | |
EnumClass = _map[name] | |
if isinstance(value, ctypes.Array): | |
return [EnumClass(x) for x in value] | |
else: | |
return EnumClass(value) | |
else: | |
return value | |
def __str__(self): | |
result = [] | |
result.append("struct {0} {{".format(self.__class__.__name__)) | |
for field in self._fields_: | |
attr, attrType = field | |
if attr in self._map: | |
attrType = self._map[attr] | |
value = getattr(self, attr) | |
result.append(" {0} [{1}] = {2!r};".format(attr, attrType.__name__, value)) | |
result.append("};") | |
return '\n'.join(result) | |
__repr__ = __str__ | |
## | |
## --- SNIP | |
## Example Code follows: | |
## | |
class TrControl(enum.IntEnum): | |
TR_ENABLE = 0 | |
TR_DISABLE = 1 | |
TR_RESET = 2 | |
class TrMode(enum.IntEnum): | |
TR_HISTORY = 0 | |
TR_FUTURE = 1 | |
TR_SHOT = 2 | |
TR_COLLECT = 3 | |
class TrAction(enum.IntEnum): | |
TR_FETCH = 0 | |
TR_ALL_CYCLE = 1 | |
class TrParameter(StructureWithEnums): | |
_fields_ = [ | |
("foo", ctypes.c_uint32), | |
("trControl", ctypes.c_int), | |
("trMode", ctypes.c_int), | |
("trAction", ctypes.c_int), | |
] | |
_map = { | |
"trControl": TrControl, "trMode": TrMode, "trAction": TrAction | |
} | |
param = TrParameter(4711, TrControl.TR_ENABLE, TrMode.TR_FUTURE, TrAction.TR_FETCH) | |
print(param) | |
# >>> struct TrParameter { | |
# foo [c_ulong] = 4711L; | |
# trControl [TrControl] = <TrControl.TR_ENABLE: 0>; | |
# trMode [TrMode] = <TrMode.TR_FUTURE: 1>; | |
# trAction [TrAction] = <TrAction.TR_FETCH: 0>; | |
# }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment