Skip to main content

bytestruct

Python application Pypi version Docs

bytestruct parses and builds binary wire formats with the standard library alone. Fixed-size records are dataclasses whose field annotations give each field's wire type; variable-length data is described by small codecs that compose like construct's. Everything is fully typed.

The layout of a record is compiled into struct.Struct objects once, when the class is defined, so parsing a record is a single unpack_from per byte-order run.

Documentation: https://doronz88.github.io/bytestruct/, including a migration guide from construct.

Install

python3 -m pip install -U bytestruct

Fixed-size records

from dataclasses import dataclass, field
from enum import IntEnum
from typing import Annotated

from bytestruct import BinaryStruct, Const, PaddedString, u16, u32


class Kind(IntEnum):
    FILE = 1
    DIRECTORY = 2


@dataclass
class Header(BinaryStruct, byte_order="<"):
    magic: Annotated[u32, Const(0x1F3D5B79)] = field(default=0x1F3D5B79, init=False)
    size: u32
    name: Annotated[str, PaddedString(16)]
    kind: Annotated[Kind, u16]  # parsed into the enum type


data = Header(size=1024, name="notes.txt", kind=Kind.FILE).build()  # a bad value -> BinaryBuildError
header = Header.parse(data)  # short input -> TruncatedDataError, wrong magic -> UnexpectedConstantError
assert header.kind is Kind.FILE

Wire fields are positional __init__ parameters in wire order. A field declared = field(default=..., init=False) is left out of __init__: parse skips its bytes (padding) and build packs its default (on a Const field, the constant). Fields without a wire type may follow the wire fields and are ignored by parse/build. Records cannot be subclassed.

Available field types include u8..u64, i16..i64, byte-order pinned u32le/u32be, Bytes(n), PaddedString(n), uuid_t, ipv4/ipv6, Array(element, n), int-enum fields and Const magics; custom ones subclass Format.

Codecs

from bytestruct import CSTRING, U32_LE, U64_LE, Aligned, Prefixed, Switch

STRING = Aligned(4, Prefixed(U32_LE, CSTRING))  # u32 length, NUL-terminated UTF-8, padded to 4 bytes
OBJECT = Switch(U32_LE, {1: STRING, 2: U64_LE})  # parses to (tag, value)

assert OBJECT.parse(OBJECT.build((1, "hi"))) == (1, "hi")

A codec is anything with read(reader) and build(value): the combinators (Prefixed, Aligned, PrefixedArray, Tuple, Switch), the leaves (CSTRING, GREEDY_STRING, GREEDY_BYTES, PASS), the scalars (U8, U16_LE/U16_BE, ..., F64_BE), Bytes(n), and record classes themselves. A record field annotated with a codec (name: Annotated[str, CSTRING]) makes the record variable-length; such fields follow the fixed-size ones. Formats with no declarative shape are read by hand with ByteReader, whose sub-readers are windows on the same buffer (nothing is copied).

Errors

Every failure raises a BinaryDataError (a ValueError): BinaryParseError (with TruncatedDataError and UnexpectedConstantError) or BinaryBuildError. Errors carry the record, field, absolute offset and the path through nested codecs, and prefix their message with them:

Header.magic at offset 0: expected 0x1f3d5b79, got 0x0
DICTIONARY -> [2] -> STRING at offset 104: expected 200 bytes, found 16

Performance

Parsing and building run mostly inside struct's C code, and conversions cost a Python call only on the fields that need one. In the benchmark (Apple M5 Pro, CPython 3.12, construct 2.10.70):

Case bytestruct construct construct, compiled
header parse 0.30 µs 2.14 µs (7.2x) 0.85 µs (2.9x)
header build 0.16 µs 2.04 µs (12.8x) 1.15 µs (7.2x)
100 strings parse 26.5 µs 133.7 µs (5.0x) 84.3 µs (3.2x)
100 strings build 11.5 µs 47.2 µs (4.1x) 41.5 µs (3.6x)

That is 4-13x faster than construct, and 3-7x faster than construct's compile(). A hand-written struct.unpack_from into a plain dataclass is still about twice as fast for one small header; that gap pays for typed fields, constant checks and located errors.

Contributing

See CONTRIBUTING.md for the development setup, the checks CI runs and how releases are cut.

License

MIT

Metadata

Release files for bytestruct 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bytestruct 0.1.1
File Size Uploaded
bytestruct-0.1.1.tar.gz 64.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bytestruct 0.1.1
File Interpreter ABI Platform
bytestruct-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 82.5 kB

Release files / bytestruct-0.1.1.tar.gz

Download URL bytestruct-0.1.1.tar.gz
Size 64.7 kB
Tags Source
SHA-256 checksum
How to use checksums
663d8e27803d0e8130734b5f97b4d2900b6d01821e8c736ba2bcda3f3b391e94
BLAKE2b-256 checksum
How to use checksums
28c8d6662dbe130b9a4a85736f24f4f2278a81665bc5714f4e27f2765226bcbe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / bytestruct-0.1.1-py3-none-any.whl

Download URL bytestruct-0.1.1-py3-none-any.whl
Size 17.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81ad058c59a317100a152063da4b29404f07ba5dc3f11bc075760e65fa7bbdf3
BLAKE2b-256 checksum
How to use checksums
a729a42a1285e1e59ac22c974e1a5be1f339dbd69abedc5956c79c5087c37b75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page