dataclasses-struct
A simple Python package that combines
dataclasses
with
struct
for packing and
unpacking Python dataclasses to fixed-length bytes
representations.
Installation
This package is available on pypi:
To work correctly with mypy
, an extension is
required; add to your mypy.ini
:
(See the guide for more details on type checking.)
Quick start
By default, dataclass-structs use native sizes, alignment, and byte ordering (endianness).
import dataclasses
from typing import Annotated
import dataclasses_struct as dcs # (1)!
@dcs.dataclass_struct(size="native", byteorder="native") # (2)!
class Vector2d:
x: dcs.F64 # (3)!
y: float #(4)!
@dcs.dataclass_struct(kw_only=True) #(5)!
class Object:
position: Vector2d #(6)!
velocity: Vector2d = dataclasses.field( # (7)!
default_factory=lambda: Vector2d(0, 0)
)
name: Annotated[bytes, 8] #(8)!
- This convention of importing
dataclasses_struct
under the aliasdcs
is used throughout these docs, but you don't have to follow this if you don't want to. - The
size
andbyteorder
keyword arguments control the size, alignment, and endianness of the class' packed binary representation. The default mode"native"
is native for both arguments. - A double precision floating point, equivalent to
double
in C. - The builtin
float
type is an alias todcs.F64
. - The
dataclass_struct
decorator supports most of the keyword arguments supported by the stdlibdataclass
decorator. - Classes decorated with
dcs.dataclass_struct
can be used as fields in other dataclass-structs provided they have the same size and byteorder modes. - The stdlib
dataclasses.field
function can be used for more complex field configurations, such as using a mutable value as a field default. - Fixed-length
bytes
arrays can be represented by annotating a field with a non-zero positive integer usingtyping.Annotated
. Values longer than the length will be truncated and values shorted will be zero-padded.
Instances of decorated classes have a
pack
method that returns
the packed representation of the object in bytes
:
>>> obj = Object(position=Vector2d(1.5, -5.6), name=b"object1")
>>> obj
Object(position=Vector2d(x=1.5, y=-5.6), velocity=Vector2d(x=0, y=0), name=b'object1')
>>> packed = obj.pack()
>>> packed
b'\x00\x00\x00\x00\x00\x00\xf8?ffffff\x16\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00object1\x00'
Decorated classes have an
from_packed
class
method that takes the packed representation and returns an instance of the
class:
>>> Object.from_packed(packed)
Object(position=Vector2d(x=1.5, y=-5.6), velocity=Vector2d(x=0.0, y=0.0), name=b'object1\x00')
In size="native"
mode, integer type names follow the standard C integer type
names:
@dcs.dataclass_struct()
class NativeIntegers:
c_int: dcs.Int
c_int_alias: int # (1)!
c_unsigned_short: dcs.UnsignedShort
void_pointer: dcs.Pointer # (2)!
size_t: dcs.UnsignedSize
# etc.
- Alias to
dcs.Int
. - Equivalent to
void *
pointer in C.
In size="std"
mode, integer type names follow the standard fixed-width
integer type names in
C:
@dcs.dataclass_struct(size="std")
class StdIntegers:
int8_t: dcs.I8
int32_t: dcs.I32
uint64_t: dcs.U64
# etc.
See the guide for the full list of supported field types.