# cdataclass - Python dataclass for C structure
## Overview
Integration 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
```bash
$ pip install cdataclass
```
## Examples
```python
import ctypes
from dataclasses import dataclass, field
from typing import List
from cdataclass import BigEndianCDataMixIn, meta
# Simple big endian C structure
@dataclass
class Item(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 dataclass
item = Item(1, b"test")
assert item.f_item_number == 1
assert item.f_item_bytes == b"test"
# Get corresponding ctypes.Structure class
c_item_class = Item.ctype()
assert issubclass(c_item_class, ctypes.BigEndianStructure)
assert hasattr(c_item_class, "_fields_")
assert hasattr(c_item_class, "_pack_")
# Get the size of corresponding ctypes.Structure class
assert Item.size() == 14 # uint32(4 bytes) + c_char * 10(10 bytes) = 14
# Convert to ctype.Structure instance
c_item = item.to_ctype()
assert isinstance(c_item, ctypes.BigEndianStructure)
assert c_item.f_item_number == 1
assert c_item.f_item_bytes == b"test"
# Serialize/Deserialize to/from buffer
hex_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)
assert item.f_item_number == 100
assert item.f_item_bytes == b"ABCDEFGHIJ"
assert item.to_bytearray() == buffer
# Serialize/Deserialize to/from immutable buffer
immutable_buffer = bytes(buffer)
item = Item.from_buffer_copy(immutable_buffer)
assert item.f_item_number == 100
assert item.f_item_bytes == b"ABCDEFGHIJ"
assert item.to_bytes() == immutable_buffer
# Use custom ecoding/decoding functions for data conversion
@dataclass
class CustomItem(BigEndianCDataMixIn):
f_number: int = field(
metadata=meta(
ctypes.c_char * 10,
encoder=lambda v: str(v).rjust(10).encode("utf-8"),
decoder=lambda v: int(v.decode("utf-8")),
)
)
f_string: str = field(
metadata=meta(
ctypes.c_char * 10,
encoder=lambda v: v.encode("utf-8"),
decoder=lambda v: v.decode("utf-8"),
)
)
custom_item = CustomItem(100, "test")
# Encode as specified
c_custom_item = custom_item.to_ctype()
assert c_custom_item.f_number == b" 100"
assert c_custom_item.f_string == b"test"
# Decode as specified
custom_item = CustomItem.from_buffer_copy(custom_item.to_bytes())
assert custom_item.f_number == 100
assert custom_item.f_string == "test"
# Nested structure
@dataclass
class Data(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 customized
f_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")) for i in range(5)],
[i for i in range(5)],
)
# Access the nested field
c_data = data.to_ctype()
assert c_data.f_item.f_item_number == 100
assert c_data.f_item.f_item_bytes == b"item"
# Access the nested array
assert c_data.f_items[0].f_item_number == 0
assert c_data.f_items[0].f_item_bytes == b"item0"
assert c_data.f_items[4].f_item_number == 4
assert c_data.f_items[4].f_item_bytes == b"item4"
assert c_data.f_int_array[0] == 0
assert c_data.f_int_array[4] == 4
```
Raw data
{
"_id": null,
"home_page": "https://github.com/hajoks/cdata",
"name": "cdataclass",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "dataclasses ctypes",
"author": "hajoks",
"author_email": "syari4369@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/4a/0a/c5c2cf2aca25cde06ccac1a40e29fcaef5e5805eae9af4e1320c7c38c36f/cdataclass-0.1.2.tar.gz",
"platform": null,
"description": "# cdataclass - Python dataclass for C structure\n\n## Overview\n\nIntegration of python dataclass and ctypes.Structure.\n\nThis library provides some API for encoding/decoding dataclasses into/from ctypes.Structure/ctypes.Union.\n\nThis library can be used for:\n\n- handling packed binary data.\n- bridging the C API and python application.\n\n## Installation\n```bash\n$ pip install cdataclass\n```\n\n## Examples\n\n```python\n\nimport ctypes\nfrom dataclasses import dataclass, field\nfrom typing import List\n\nfrom cdataclass import BigEndianCDataMixIn, meta\n\n\n# Simple big endian C structure\n@dataclass\nclass Item(BigEndianCDataMixIn):\n f_item_number: int = field(metadata=meta(ctypes.c_uint32))\n f_item_bytes: bytes = field(metadata=meta(ctypes.c_char * 10))\n\n\n# Use as normal dataclass\nitem = Item(1, b\"test\")\nassert item.f_item_number == 1\nassert item.f_item_bytes == b\"test\"\n\n\n# Get corresponding ctypes.Structure class\nc_item_class = Item.ctype()\nassert issubclass(c_item_class, ctypes.BigEndianStructure)\nassert hasattr(c_item_class, \"_fields_\")\nassert hasattr(c_item_class, \"_pack_\")\n\n\n# Get the size of corresponding ctypes.Structure class\nassert Item.size() == 14 # uint32(4 bytes) + c_char * 10(10 bytes) = 14\n\n\n# Convert to ctype.Structure instance\nc_item = item.to_ctype()\nassert isinstance(c_item, ctypes.BigEndianStructure)\nassert c_item.f_item_number == 1\nassert c_item.f_item_bytes == b\"test\"\n\n\n# Serialize/Deserialize to/from buffer\nhex_str_represents_uint32_100 = \"00 00 00 64\"\nhex_str_represents_char_ABCDEFGHIJ = \"41 42 43 44 45 46 47 48 49 4A\"\nbuffer = bytearray.fromhex(hex_str_represents_uint32_100 + \" \" + hex_str_represents_char_ABCDEFGHIJ)\nitem = Item.from_buffer(buffer)\nassert item.f_item_number == 100\nassert item.f_item_bytes == b\"ABCDEFGHIJ\"\nassert item.to_bytearray() == buffer\n\n\n# Serialize/Deserialize to/from immutable buffer\nimmutable_buffer = bytes(buffer)\nitem = Item.from_buffer_copy(immutable_buffer)\nassert item.f_item_number == 100\nassert item.f_item_bytes == b\"ABCDEFGHIJ\"\nassert item.to_bytes() == immutable_buffer\n\n\n# Use custom ecoding/decoding functions for data conversion\n@dataclass\nclass CustomItem(BigEndianCDataMixIn):\n f_number: int = field(\n metadata=meta(\n ctypes.c_char * 10,\n encoder=lambda v: str(v).rjust(10).encode(\"utf-8\"),\n decoder=lambda v: int(v.decode(\"utf-8\")),\n )\n )\n f_string: str = field(\n metadata=meta(\n ctypes.c_char * 10,\n encoder=lambda v: v.encode(\"utf-8\"),\n decoder=lambda v: v.decode(\"utf-8\"),\n )\n )\n\n\ncustom_item = CustomItem(100, \"test\")\n\n# Encode as specified\nc_custom_item = custom_item.to_ctype()\nassert c_custom_item.f_number == b\" 100\"\nassert c_custom_item.f_string == b\"test\"\n\n# Decode as specified\ncustom_item = CustomItem.from_buffer_copy(custom_item.to_bytes())\nassert custom_item.f_number == 100\nassert custom_item.f_string == \"test\"\n\n\n# Nested structure\n@dataclass\nclass Data(BigEndianCDataMixIn):\n f_number: int = field(metadata=meta(ctypes.c_uint32))\n f_bytes: bytes = field(metadata=meta(ctypes.c_char * 20))\n # Use cls.ctype() to define a nested structure or array of structure or whatever customized\n f_item: Item = field(metadata=meta(Item.ctype()))\n f_items: List[Item] = field(metadata=meta(Item.ctype() * 5))\n f_int_array: List[int] = field(metadata=meta(ctypes.c_uint16 * 5))\n\n\ndata = Data(\n 1,\n b\"data\",\n Item(100, b\"item\"),\n [Item(i, bytes(f\"item{i}\", \"utf-8\")) for i in range(5)],\n [i for i in range(5)],\n)\n\n# Access the nested field\nc_data = data.to_ctype()\nassert c_data.f_item.f_item_number == 100\nassert c_data.f_item.f_item_bytes == b\"item\"\n\n# Access the nested array\nassert c_data.f_items[0].f_item_number == 0\nassert c_data.f_items[0].f_item_bytes == b\"item0\"\nassert c_data.f_items[4].f_item_number == 4\nassert c_data.f_items[4].f_item_bytes == b\"item4\"\n\nassert c_data.f_int_array[0] == 0\nassert c_data.f_int_array[4] == 4\n\n```\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Integration of ctypes and dataclasses",
"version": "0.1.2",
"split_keywords": [
"dataclasses",
"ctypes"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "d5d98fd79713fa0258bbc22f35e69df9bdb5951c17603ec4f27deb9fb8d9c93f",
"md5": "c1951d893152006f105272c2acc55f17",
"sha256": "22311fed1c298b8eed9987f8b2577e6c19b319365704d95b9333c360bd56256e"
},
"downloads": -1,
"filename": "cdataclass-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c1951d893152006f105272c2acc55f17",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 7075,
"upload_time": "2023-03-11T15:29:06",
"upload_time_iso_8601": "2023-03-11T15:29:06.688594Z",
"url": "https://files.pythonhosted.org/packages/d5/d9/8fd79713fa0258bbc22f35e69df9bdb5951c17603ec4f27deb9fb8d9c93f/cdataclass-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "4a0ac5c2cf2aca25cde06ccac1a40e29fcaef5e5805eae9af4e1320c7c38c36f",
"md5": "0dfbcbdc81bdca2223b92f2c5ff03cfa",
"sha256": "8aa4ba8781d09dbdc6abeb479a2cc361bf179be6f71ca3b82437a1ac0c0423a2"
},
"downloads": -1,
"filename": "cdataclass-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "0dfbcbdc81bdca2223b92f2c5ff03cfa",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 7100,
"upload_time": "2023-03-11T15:29:09",
"upload_time_iso_8601": "2023-03-11T15:29:09.002515Z",
"url": "https://files.pythonhosted.org/packages/4a/0a/c5c2cf2aca25cde06ccac1a40e29fcaef5e5805eae9af4e1320c7c38c36f/cdataclass-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-03-11 15:29:09",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "hajoks",
"github_project": "cdata",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "cdataclass"
}