Skip to main content

Integration of ctypes and dataclasses

Project description

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.

Examples

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cdataclass-0.1.0.tar.gz (7.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cdataclass-0.1.0-py3-none-any.whl (7.1 kB view details)

Uploaded Python 3

File details

Details for the file cdataclass-0.1.0.tar.gz.

File metadata

  • Download URL: cdataclass-0.1.0.tar.gz
  • Upload date:
  • Size: 7.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.0

File hashes

Hashes for cdataclass-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ebbe6a284020f313ac69e6a530b7bf039779616b9d10f53f6b0ae9d071efd2a1
MD5 5f7f8c1c57f9d5933188ff45cd357787
BLAKE2b-256 5f7dfbae07d5f0683f451545a240ba28e462ef4a77c1a9d602bb0f9b4cf64225

See more details on using hashes here.

File details

Details for the file cdataclass-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cdataclass-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.10.0

File hashes

Hashes for cdataclass-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 531253c89623c3f20820e5de1bd85b99d43e9b701b423b15bb6f5697e0b4ab37
MD5 d5889fc2adb85bbca5b269cd7d335bf7
BLAKE2b-256 b6ec636eb0814694c96a4a63643969c182c1f7f567cc9ddb6c5e738931914b6a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page