Package: cdataclass
cdataclass - Python dataclass for C structureOverviewIntegration of python dataclass and ctypes.Structure.This library provides some API for encoding/decoding dataclasses into/from ctypes.Structure/ctypes.Union.This library can be used for:handling packed binary data.bridging the C API and python application.Installation$pipinstallcdataclassExamplesimportctypesfromdataclassesimportdataclass,fieldfromtypingimportListfromcdataclassimportBigEndianCDataMixIn,meta# Simple big endian C structure@dataclassclassItem(BigEndianCDataMixIn):f_item_number:int=field(metadata=meta(ctypes.c_uint32))f_item_bytes:bytes=field(metadata=meta(ctypes.c_char*10))# Use as normal dataclassitem=Item(1,b"test")assertitem.f_item_number==1assertitem.f_item_bytes==b"test"# Get corresponding ctypes.Structure classc_item_class=Item.ctype()assertissubclass(c_item_class,ctypes.BigEndianStructure)asserthasattr(c_item_class,"_fields_")asserthasattr(c_item_class,"_pack_")# Get the size of corresponding ctypes.Structure classassertItem.size()==14# uint32(4 bytes) + c_char * 10(10 bytes) = 14# Convert to ctype.Structure instancec_item=item.to_ctype()assertisinstance(c_item,ctypes.BigEndianStructure)assertc_item.f_item_number==1assertc_item.f_item_bytes==b"test"# Serialize/Deserialize to/from bufferhex_str_represents_uint32_100="00 00 00 64"hex_str_represents_char_ABCDEFGHIJ="41 42 43 44 45 46 47 48 49 4A"buffer=bytearray.fromhex(hex_str_represents_uint32_100+" "+hex_str_represents_char_ABCDEFGHIJ)item=Item.from_buffer(buffer)assertitem.f_item_number==100assertitem.f_item_bytes==b"ABCDEFGHIJ"assertitem.to_bytearray()==buffer# Serialize/Deserialize to/from immutable bufferimmutable_buffer=bytes(buffer)item=Item.from_buffer_copy(immutable_buffer)assertitem.f_item_number==100assertitem.f_item_bytes==b"ABCDEFGHIJ"assertitem.to_bytes()==immutable_buffer# Use custom ecoding/decoding functions for data conversion@dataclassclassCustomItem(BigEndianCDataMixIn):f_number:int=field(metadata=meta(ctypes.c_char*10,encoder=lambdav:str(v).rjust(10).encode("utf-8"),decoder=lambdav:int(v.decode("utf-8")),))f_string:str=field(metadata=meta(ctypes.c_char*10,encoder=lambdav:v.encode("utf-8"),decoder=lambdav:v.decode("utf-8"),))custom_item=CustomItem(100,"test")# Encode as specifiedc_custom_item=custom_item.to_ctype()assertc_custom_item.f_number==b"       100"assertc_custom_item.f_string==b"test"# Decode as specifiedcustom_item=CustomItem.from_buffer_copy(custom_item.to_bytes())assertcustom_item.f_number==100assertcustom_item.f_string=="test"# Nested structure@dataclassclassData(BigEndianCDataMixIn):f_number:int=field(metadata=meta(ctypes.c_uint32))f_bytes:bytes=field(metadata=meta(ctypes.c_char*20))# Use cls.ctype() to define a nested structure or array of structure or whatever customizedf_item:Item=field(metadata=meta(Item.ctype()))f_items:List[Item]=field(metadata=meta(Item.ctype()*5))f_int_array:List[int]=field(metadata=meta(ctypes.c_uint16*5))data=Data(1,b"data",Item(100,b"item"),[Item(i,bytes(f"item{i}","utf-8"))foriinrange(5)],[iforiinrange(5)],)# Access the nested fieldc_data=data.to_ctype()assertc_data.f_item.f_item_number==100assertc_data.f_item.f_item_bytes==b"item"# Access the nested arrayassertc_data.f_items[0].f_item_number==0assertc_data.f_items[0].f_item_bytes==b"item0"assertc_data.f_items[4].f_item_number==4assertc_data.f_items[4].f_item_bytes==b"item4"assertc_data.f_int_array[0]==0assertc_data.f_int_array[4]==4