I'm trying to create a ctypes generic structure which has the behavior of a dynamic array. I'm using the following SO answer as a base: https://stackoverflow.com/a/42843610/1935787
It works when I instantiate the class independently, but when I use this class as field of another class, the from_buffer_copy
does not call the __new__
class method, so my class is not creating the array.
Code:
import ctypes
class SizedStructure(ctypes.LittleEndianStructure):
_sized_vars_ = [] # (var_name, var_type, size_var_id, (size_var_path,), size_var_granularity)
@classmethod
def from_buffer_copy(cls, buff):
obj = type(cls).from_buffer_copy(cls, buff)
values = cls.get_object_sized_vars_values(obj)
instance = cls.create_inner_struct(values).from_buffer_copy(buff)
return instance
@classmethod
def create_inner_struct(cls, values):
fields = cls._fields_[:]
class_name = cls.__name__
for var_name, var_type, size_var_id, _, size_var_granularity in cls._sized_vars_:
if size_var_id not in values:
raise Exception("'{}' not passed".format(size_var_id))
fields.append((var_name, var_type * (values[size_var_id] * size_var_granularity)))
class_name = class_name + "_{}_{}".format(size_var_id, values[size_var_id])
class Temp(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = fields
def __init__(self, *args, **kwargs):
super(Temp, self).__init__(*args, **kwargs)
for key, value in kwargs.items():
setattr(self, key, value)
return type(class_name, (Temp,), {})
@classmethod
def get_object_sized_vars_values(cls, obj):
values = {}
for _, _, size_var_id, size_var_path, _ in cls._sized_vars_:
curr = obj
for part in size_var_path:
curr = getattr(curr, part)
values[size_var_id] = curr
return values
class DynamicLengthArray(SizedStructure):
_pack_ = 1
_fields_ = [
("size", ctypes.c_uint32),
]
_sized_vars_ = [("data", ctypes.c_ubyte, "size", ("size",), ctypes.sizeof(ctypes.c_uint8))]
class DynamicLengthArrayParent(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
("id", ctypes.c_uint32),
("arr", DynamicLengthArray),
]
a = DynamicLengthArray.from_buffer_copy(b"\x02\x00\x00\x00\x33\x44")
assert(a.size == 2)
assert(a.data[0] == 0x33)
assert(a.data[1] == 0x44)
print(a.data)
b = DynamicLengthArrayParent.from_buffer_copy(b"\x78\x56\x34\x12\x02\x00\x00\x00\x33\x44")
assert(b.id == 0x12345678)
assert(b.arr.size == 2)
assert(b.arr.data[0] == 0x33)
assert(b.arr.data[1] == 0x44)
print(b.arr.data)
Result:
<__main__.c_ubyte_Array_2 object at 0x00000000029EB6C8>
Traceback (most recent call last):
File "c:/Temp/so.py", line 67, in <module>
assert(b.arr.data[0] == 0x33)
AttributeError: 'DynamicLengthArray' object has no attribute 'data'
How can I trigger the __new__
method by calling from_buffer_copy
?