Skip to content

API reference

DataclassStructInternal

Bases: Generic[T]

Source code in dataclasses_struct/dataclass.py
class DataclassStructInternal(Generic[T]):
    struct: Struct
    cls: type[T]
    _fields: list[_FieldInfo]

    @property
    def format(self) -> str:
        """
        The format string used by the `struct` module to pack/unpack data.

        See https://docs.python.org/3/library/struct.html#format-strings.
        """
        return self.struct.format

    @property
    def size(self) -> int:
        """Size of the packed representation in bytes."""
        return self.struct.size

    @property
    def mode(self) -> str:
        """
        The `struct` mode character that determines size, alignment, and
        byteorder.

        This is the first character of the `format` field. See
        https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment
        for more info.
        """
        return self.format[0]

    def __init__(
        self,
        fmt: str,
        cls: type,
        fields: list[_FieldInfo],
    ):
        self.struct = Struct(fmt)
        self.cls = cls
        self._fields = fields

    def _flattened_attrs(self, outer_self: T) -> list[Any]:
        """
        Returns a list of all attributes of `outer_self`, including those of
        any nested structs.
        """
        attrs: list[Any] = []
        for field in self._fields:
            attr = getattr(outer_self, field.name)
            self._flatten_attr(attrs, attr)
        return attrs

    @staticmethod
    def _flatten_attr(attrs: list[Any], attr: object) -> None:
        if is_dataclass_struct(attr):
            attrs.extend(attr.__dataclass_struct__._flattened_attrs(attr))
        elif isinstance(attr, list):
            for sub_attr in attr:
                DataclassStructInternal._flatten_attr(attrs, sub_attr)
        else:
            attrs.append(attr)

    def _pack(self, obj: T) -> bytes:
        return self.struct.pack(*self._flattened_attrs(obj))

    def _arg_generator(self, args: Iterator) -> Generator:
        for field in self._fields:
            yield from DataclassStructInternal._generate_args_recursively(
                args, field.field, field.type_
            )

    @staticmethod
    def _generate_args_recursively(
        args: Iterator,
        field: Field[Any],
        field_type: type,
    ) -> Generator:
        if is_dataclass_struct(field_type):
            yield field_type.__dataclass_struct__._init_from_args(args)
        elif isinstance(field, _FixedLengthArrayField):
            items: list = []
            for _ in range(field.n):
                items.extend(
                    DataclassStructInternal._generate_args_recursively(
                        args, field.item_field, field.item_type
                    )
                )
            yield items
        else:
            yield field_type(next(args))

    def _init_from_args(self, args: Iterator) -> T:
        """
        Returns an instance of self.cls, consuming args
        """
        kwargs = {}
        no_init_args = {}

        for field, arg in zip(self._fields, self._arg_generator(args)):
            if field.init:
                kwargs[field.name] = arg
            else:
                no_init_args[field.name] = arg

        obj = self.cls(**kwargs)
        for name, arg in no_init_args.items():
            setattr(obj, name, arg)
        return obj

    def _unpack(self, data: Buffer) -> T:
        return self._init_from_args(iter(self.struct.unpack(data)))

format property

The format string used by the struct module to pack/unpack data.

See https://docs.python.org/3/library/struct.html#format-strings.

mode property

The struct mode character that determines size, alignment, and byteorder.

This is the first character of the format field. See https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment for more info.

size property

Size of the packed representation in bytes.

DataclassStructProtocol

Bases: Protocol

Source code in dataclasses_struct/dataclass.py
class DataclassStructProtocol(Protocol):
    __dataclass_struct__: ClassVar[DataclassStructInternal]
    """
    Internal data used by the library for packing and unpacking structs.

    See
    [`DataclassStructInternal`][dataclasses_struct.DataclassStructInternal].
    """

    @classmethod
    def from_packed(cls: type[T], data: Buffer) -> T:
        """Return an instance of the class from its packed representation.

        Args:
            data: The packed representation of the class as returned by
                [`pack`][dataclasses_struct.DataclassStructProtocol.pack].

        Returns:
            An instance of the class unpacked from `data`.

        Raises:
            struct.error: If `data` is the wrong length.
        """
        ...

    def pack(self) -> bytes:
        """Return the packed representation in `bytes` of the object.

        Returns:
            The packed representation. Can be used to instantiate a new object
                with
                [`from_packed`][dataclasses_struct.DataclassStructProtocol.from_packed].

        Raises:
            struct.error: If any of the fields are out of range or the wrong
                type.
        """
        ...

__dataclass_struct__ class-attribute

Internal data used by the library for packing and unpacking structs.

See DataclassStructInternal.

from_packed(data) classmethod

Return an instance of the class from its packed representation.

Parameters:

Name Type Description Default
data Buffer

The packed representation of the class as returned by pack.

required

Returns:

Type Description
T

An instance of the class unpacked from data.

Raises:

Type Description
error

If data is the wrong length.

Source code in dataclasses_struct/dataclass.py
@classmethod
def from_packed(cls: type[T], data: Buffer) -> T:
    """Return an instance of the class from its packed representation.

    Args:
        data: The packed representation of the class as returned by
            [`pack`][dataclasses_struct.DataclassStructProtocol.pack].

    Returns:
        An instance of the class unpacked from `data`.

    Raises:
        struct.error: If `data` is the wrong length.
    """
    ...

pack()

Return the packed representation in bytes of the object.

Returns:

Type Description
bytes

The packed representation. Can be used to instantiate a new object with from_packed.

Raises:

Type Description
error

If any of the fields are out of range or the wrong type.

Source code in dataclasses_struct/dataclass.py
def pack(self) -> bytes:
    """Return the packed representation in `bytes` of the object.

    Returns:
        The packed representation. Can be used to instantiate a new object
            with
            [`from_packed`][dataclasses_struct.DataclassStructProtocol.from_packed].

    Raises:
        struct.error: If any of the fields are out of range or the wrong
            type.
    """
    ...

dataclass_struct(*, size='native', byteorder='native', validate_defaults=True, **dataclass_kwargs)

dataclass_struct(
    *,
    size: Literal["native"] = "native",
    byteorder: Literal["native"] = "native",
    validate_defaults: bool = True,
    **dataclass_kwargs: Unpack[DataclassKwargs],
) -> Callable[[type], type]
dataclass_struct(
    *,
    size: Literal["std"],
    byteorder: Literal[
        "native", "big", "little", "network"
    ] = "native",
    validate_defaults: bool = True,
    **dataclass_kwargs: Unpack[DataclassKwargs],
) -> Callable[[type], type]

Create a dataclass struct.

Should be used as a decorator on a class:

import dataclasses_struct as dcs

@dcs.dataclass_struct()
class A:
    data: dcs.Pointer
    size: dcs.UnsignedSize

The allowed size and byteorder argument combinations are as as follows.

size byteorder Notes
"native" "native" The default. Native alignment and padding.
"std" "native" Standard integer sizes and system endianness, no alignment/padding.
"std" "little" Standard integer sizes and little endian, no alignment/padding.
"std" "big" Standard integer sizes and big endian, no alignment/padding.
"std" "network" Equivalent to byteorder="big".

Parameters:

Name Type Description Default
size Literal['native', 'std']

The size mode.

'native'
byteorder Literal['native', 'big', 'little', 'network']

The byte order of the generated struct. If size="native", only "native" is allowed.

'native'
validate_defaults bool

Whether to validate the default values of any fields.

True
dataclass_kwargs Unpack[DataclassKwargs]

Any additional keyword arguments to pass to the stdlib dataclass decorator. The slots and weakref_slot keyword arguments are not supported.

{}

Raises:

Type Description
ValueError

If the size and byteorder args are invalid or if validate_defaults=True and any of the fields' default values are invalid for their type.

TypeError

If any of the fields' type annotations are invalid or not supported.

Source code in dataclasses_struct/dataclass.py
@dataclass_transform()
def dataclass_struct(
    *,
    size: Literal["native", "std"] = "native",
    byteorder: Literal["native", "big", "little", "network"] = "native",
    validate_defaults: bool = True,
    **dataclass_kwargs: Unpack[DataclassKwargs],
) -> Callable[[type], type]:
    """Create a dataclass struct.

    Should be used as a decorator on a class:

    ```python
    import dataclasses_struct as dcs

    @dcs.dataclass_struct()
    class A:
        data: dcs.Pointer
        size: dcs.UnsignedSize
    ```

    The allowed `size` and `byteorder` argument combinations are as as follows.

    | `size`     | `byteorder` | Notes                                                               |
    | ---------- | ----------- | ------------------------------------------------------------------  |
    | `"native"` | `"native"`  | The default. Native alignment and padding.                          |
    | `"std"`    | `"native"`  | Standard integer sizes and system endianness, no alignment/padding. |
    | `"std"`    | `"little"`  | Standard integer sizes and little endian, no alignment/padding.     |
    | `"std"`    | `"big"`     | Standard integer sizes and big endian, no alignment/padding.        |
    | `"std"`    | `"network"` | Equivalent to `byteorder="big"`.                                    |

    Args:
        size: The size mode.
        byteorder: The byte order of the generated struct. If `size="native"`,
            only `"native"` is allowed.
        validate_defaults: Whether to validate the default values of any
            fields.
        dataclass_kwargs: Any additional keyword arguments to pass to the
            [stdlib
            `dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass)
            decorator. The `slots` and `weakref_slot` keyword arguments are not
            supported.

    Raises:
        ValueError: If the `size` and `byteorder` args are invalid or if
            `validate_defaults=True` and any of the fields' default values are
            invalid for their type.
        TypeError: If any of the fields' type annotations are invalid or
            not supported.
    """  # noqa: E501
    is_native = size == "native"
    if is_native:
        if byteorder != "native":
            raise ValueError("'native' size requires 'native' byteorder")
    elif size != "std":
        raise ValueError(f"invalid size: {size}")
    if byteorder not in ("native", "big", "little", "network"):
        raise ValueError(f"invalid byteorder: {byteorder}")

    for kwarg in ("slots", "weakref_slot"):
        if kwarg in dataclass_kwargs:
            msg = f"dataclass '{kwarg}' keyword argument is not supported"
            raise ValueError(msg)

    def decorator(cls: type) -> type:
        return _make_class(
            cls,
            mode=_SIZE_BYTEORDER_MODE_CHAR[(size, byteorder)],
            is_native=is_native,
            validate_defaults=validate_defaults,
            dataclass_kwargs=dataclass_kwargs,
        )

    return decorator

get_struct_size(cls_or_obj)

Get the size of the packed representation of the struct in bytes.

Parameters:

Name Type Description Default
cls_or_obj object

A class that has been decorated with dataclass_struct or an instance of one.

required

Returns:

Type Description
int

The size of the packed representation in bytes.

Raises:

Type Description
TypeError

if cls_or_obj is not a dataclass-struct.

Source code in dataclasses_struct/dataclass.py
def get_struct_size(cls_or_obj: object) -> int:
    """Get the size of the packed representation of the struct in bytes.

    Args:
        cls_or_obj: A class that has been decorated with
            [`dataclass_struct`][dataclasses_struct.dataclass_struct] or an
            instance of one.

    Returns:
        The size of the packed representation in bytes.

    Raises:
        TypeError: if `cls_or_obj` is not a dataclass-struct.
    """
    if not is_dataclass_struct(cls_or_obj):
        raise TypeError(f"{cls_or_obj} is not a dataclass_struct")
    return cls_or_obj.__dataclass_struct__.size

is_dataclass_struct(obj)

is_dataclass_struct(
    obj: type,
) -> TypeGuard[type[DataclassStructProtocol]]
is_dataclass_struct(
    obj: object,
) -> TypeGuard[DataclassStructProtocol]

Determine whether a type or object is a dataclass-struct.

Parameters:

Name Type Description Default
obj Union[type, object]

A class or object.

required

Returns:

Type Description
Union[TypeGuard[DataclassStructProtocol], TypeGuard[type[DataclassStructProtocol]]]

True if obj is a class that has been decorated with dataclass_struct or is an instance of one.

Source code in dataclasses_struct/dataclass.py
def is_dataclass_struct(
    obj: Union[type, object],
) -> Union[
    TypeGuard[DataclassStructProtocol],
    TypeGuard[type[DataclassStructProtocol]],
]:
    """Determine whether a type or object is a dataclass-struct.

    Args:
        obj: A class or object.

    Returns:
        `True` if obj is a class that has been decorated with
            [`dataclass_struct`][dataclasses_struct.dataclass_struct] or is an
            instance of one.
    """
    return (
        dataclasses.is_dataclass(obj)
        and hasattr(obj, "__dataclass_struct__")
        and isinstance(obj.__dataclass_struct__, DataclassStructInternal)
    )